diff options
67 files changed, 2235 insertions, 571 deletions
diff --git a/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt index 54ba2be39f0c..d9caa295cc6e 100644 --- a/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt +++ b/Documentation/devicetree/bindings/input/pixart-pat9125-switch.txt @@ -10,6 +10,8 @@ Required properties: - reg : i2c slave address of the device. - interrupt-parent : parent of interrupt. - interrupts : interrupt to indicate motion of the rotating switch. + - vdd-supply : Power supply needed to power up the device. + - vld-supply : Power source required to power up I2C bus. Optional properties: - pixart,inverse-x : boolean, use this to invert the x data before sending it to input framework @@ -44,6 +46,8 @@ Example: reg = <0x75>; interrupt-parent = <&msm_gpio>; interrupts = <98 0x2008>; + vdd-supply = <&pm8110_l5>; + vld-supply = <&pm8110_l17>; pixart,irq-gpio = <&msm_gpio 98 0x2008>; pinctrl-names = "pmx_rot_switch_active", "pmx_rot_switch_suspend", diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt index 12ac75a8608c..82386ba9b082 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt @@ -110,6 +110,25 @@ Charger specific properties: will use io-channel-names to match IIO input names with IIO specifiers. +- qcom,float-option + Usage: optional + Value type: <u32> + Definition: Configures how the charger behaves when a float charger is + detected by APSD + 1 - Treat as a DCP + 2 - Treat as a SDP + 3 - Disable charging + 4 - Suspend USB input + +- qcom,hvdcp-disable + Usage: optional + Value type: <empty> + Definition: Specifies if hvdcp charging is to be enabled or not. + If this property is not specified hvdcp will be enabled. + If this property is specified, hvdcp 2.0 detection will still + happen but the adapter won't be asked to switch to a higher + voltage point. + ============================================= Second Level Nodes - SMB2 Charger Peripherals ============================================= @@ -143,7 +162,6 @@ pmicobalt_charger: qcom,qpnp-smb2 { io-channels = <&pmic_rradc 0>; io-channel-names = "rradc_batt_id"; - qcom,suspend-input; dpdm-supply = <&qusb_phy0>; qcom,step-soc-thresholds = <60 70 80 90>; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi index a37fa26b1055..27e537c9c702 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-camera.dtsi @@ -391,6 +391,7 @@ <&clock_mmss clk_mmss_mnoc_ahb_clk>, <&clock_mmss clk_mmss_camss_ahb_clk>, <&clock_mmss clk_mmss_camss_top_ahb_clk>, + <&clock_mmss clk_cpp_clk_src>, <&clock_mmss clk_mmss_camss_cpp_clk>, <&clock_mmss clk_mmss_camss_cpp_ahb_clk>, <&clock_mmss clk_mmss_camss_cpp_axi_clk>, @@ -400,10 +401,11 @@ clock-names = "mmssnoc_axi_clk", "mnoc_ahb_clk", "camss_ahb_clk", "camss_top_ahb_clk", + "cpp_src_clk", "cpp_core_clk", "camss_cpp_ahb_clk", "camss_cpp_axi_clk", "micro_iface_clk", "mmss_smmu_axi_clk", "cpp_vbif_ahb_clk"; - qcom,clock-rates = <0 0 0 0 200000000 0 0 0 0 0>; + qcom,clock-rates = <0 0 0 0 200000000 200000000 0 0 0 0 0>; qcom,min-clock-rate = <200000000>; qcom,bus-master = <1>; qcom,vbif-qos-setting = <0x20 0x10000000>, diff --git a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi index f9bb6e512d33..7948dc3489cb 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-mtp.dtsi @@ -289,9 +289,11 @@ reg = <0x1000 0x700>; io-channels = <&smb138x_tadc 2>, - <&smb138x_tadc 12>; + <&smb138x_tadc 12>, + <&smb138x_tadc 3>; io-channel-names = "charger_temp", - "charger_temp_max"; + "charger_temp_max", + "batt_i"; }; }; }; diff --git a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi index 8016a3822a7f..81e2203a5e61 100644 --- a/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi +++ b/arch/arm/boot/dts/qcom/msmcobalt-v2.dtsi @@ -672,8 +672,8 @@ <1036800 444000000 0x55555555>, /* 720p@240, 1080p@120,1440p@60, * UHD@30 */ /*NOMINAL*/ < 829440 355200000 0x55555555>, /* UHD/4096x2160@30 SVSL1 */ - < 489600 269330000 0x55555555>, /* 1080p@60 SVS */ - < 432000 200000000 0x55555555>, /* 720p@120, 1080p@30 */ + < 489600 269330000 0x55555555>, /* 1080p@60, 720p@120 SVS */ + < 345600 200000000 0x55555555>, /* 2560x1440@24, 1080p@30 */ /* SVS2 */ /* Decoders */ diff --git a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi index e5db2766c553..e93067e3697c 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon-smp2p.dtsi @@ -195,4 +195,27 @@ interrupt-controller; #interrupt-cells = <2>; }; + + /* ssr - inbound entry from turing */ + smp2pgpio_ssr_smp2p_5_in: qcom,smp2pgpio-ssr-smp2p-5-in { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "slave-kernel"; + qcom,remote-pid = <5>; + qcom,is-inbound; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + /* ssr - outbound entry to turing */ + smp2pgpio_ssr_smp2p_5_out: qcom,smp2pgpio-ssr-smp2p-5-out { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "master-kernel"; + qcom,remote-pid = <5>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; }; diff --git a/arch/arm/boot/dts/qcom/msmfalcon.dtsi b/arch/arm/boot/dts/qcom/msmfalcon.dtsi index 0cc8a1420f3f..10d5bbcc01e5 100644 --- a/arch/arm/boot/dts/qcom/msmfalcon.dtsi +++ b/arch/arm/boot/dts/qcom/msmfalcon.dtsi @@ -377,9 +377,8 @@ }; }; - clock_rpmcc: qcom,dummycc { - compatible = "qcom,dummycc"; - clock-output-names = "rpmcc_clocks"; + clock_rpmcc: qcom,rpmcc { + compatible = "qcom,rpmcc-msmfalcon", "qcom,rpmcc"; #clock-cells = <1>; }; @@ -702,6 +701,38 @@ qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>; status = "ok"; }; + + qcom,turing@1a300000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x1a300000 0x00100>; + interrupts = <0 518 1>; + + vdd_cx-supply = <&pmfalcon_s3b_level>; + qcom,proxy-reg-names = "vdd_cx"; + qcom,vdd_cx-uV-uA = <RPM_SMD_REGULATOR_LEVEL_TURBO 100000>; + + clocks = <&clock_rpmcc CXO_PIL_CDSP_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <18>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <423>; + qcom,sysmon-id = <7>; + qcom,ssctl-instance-id = <0x17>; + qcom,firmware-name = "cdsp"; + memory-region = <&cdsp_fw_mem>; + + /* GPIO inputs from turing */ + qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_5_in 0 0>; + qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_5_in 2 0>; + qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_5_in 1 0>; + qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_5_in 3 0>; + + /* GPIO output to turing*/ + qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_5_out 0 0>; + status = "ok"; + }; }; #include "msmfalcon-ion.dtsi" diff --git a/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi b/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi index 695a4f3b63c7..1a72414de094 100644 --- a/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi +++ b/arch/arm/boot/dts/qcom/msmtriton-smp2p.dtsi @@ -133,4 +133,27 @@ compatible = "qcom,smp2pgpio-sleepstate-out"; gpios = <&smp2pgpio_sleepstate_2_out 0 0>; }; + + /* ssr - inbound entry from lpass */ + smp2pgpio_ssr_smp2p_2_in: qcom,smp2pgpio-ssr-smp2p-2-in { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "slave-kernel"; + qcom,remote-pid = <2>; + qcom,is-inbound; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; + + /* ssr - outbound entry to lpass */ + smp2pgpio_ssr_smp2p_2_out: qcom,smp2pgpio-ssr-smp2p-2-out { + compatible = "qcom,smp2pgpio"; + qcom,entry-name = "master-kernel"; + qcom,remote-pid = <2>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + }; }; diff --git a/arch/arm/boot/dts/qcom/msmtriton.dtsi b/arch/arm/boot/dts/qcom/msmtriton.dtsi index 09bb5f081602..1dbefc555850 100644 --- a/arch/arm/boot/dts/qcom/msmtriton.dtsi +++ b/arch/arm/boot/dts/qcom/msmtriton.dtsi @@ -177,6 +177,14 @@ reg = <0x0 0x92a00000 0x0 0x1e00000>; }; + venus_fw_mem: venus_fw_region { + compatible = "shared-dma-pool"; + alloc-ranges = <0x0 0x80000000 0x0 0x20000000>; + reusable; + alignment = <0x0 0x400000>; + size = <0x0 0x800000>; + }; + adsp_mem: adsp_region { compatible = "shared-dma-pool"; alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>; @@ -326,9 +334,8 @@ }; }; - clock_rpmcc: qcom,dummycc { - compatible = "qcom,dummycc"; - clock-output-names = "rpmcc_clocks"; + clock_rpmcc: qcom,rpmcc { + compatible = "qcom,rpmcc-msmfalcon", "qcom,rpmcc"; #clock-cells = <1>; }; @@ -541,6 +548,38 @@ <0 425 0>; /* CE11 */ qcom,wlan-msa-memory = <0x100000>; }; + + qcom,lpass@15700000 { + compatible = "qcom,pil-tz-generic"; + reg = <0x15700000 0x00100>; + interrupts = <0 162 1>; + + vdd_cx-supply = <&pmfalcon_s3b_level>; + qcom,proxy-reg-names = "vdd_cx"; + qcom,vdd_cx-uV-uA = <RPM_SMD_REGULATOR_LEVEL_TURBO 100000>; + + clocks = <&clock_rpmcc CXO_PIL_LPASS_CLK>; + clock-names = "xo"; + qcom,proxy-clock-names = "xo"; + + qcom,pas-id = <1>; + qcom,proxy-timeout-ms = <10000>; + qcom,smem-id = <423>; + qcom,sysmon-id = <1>; + qcom,ssctl-instance-id = <0x14>; + qcom,firmware-name = "adsp"; + memory-region = <&adsp_fw_mem>; + + /* GPIO inputs from lpass */ + qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_2_in 0 0>; + qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_2_in 2 0>; + qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_2_in 1 0>; + qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_2_in 3 0>; + + /* GPIO output to lpass */ + qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>; + status = "ok"; + }; }; #include "msmtriton-ion.dtsi" diff --git a/arch/arm64/configs/msmcortex-perf_defconfig b/arch/arm64/configs/msmcortex-perf_defconfig index 799e43f09a11..5d2f29b053c4 100644 --- a/arch/arm64/configs/msmcortex-perf_defconfig +++ b/arch/arm64/configs/msmcortex-perf_defconfig @@ -535,6 +535,7 @@ CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y CONFIG_QSEE_IPC_IRQ_BRIDGE=y CONFIG_QCOM_SMCINVOKE=y +CONFIG_QCOM_EARLY_RANDOM=y CONFIG_MEM_SHARE_QMI_SERVICE=y CONFIG_QCOM_BIMC_BWMON=y CONFIG_ARM_MEMLAT_MON=y diff --git a/arch/arm64/configs/msmcortex_defconfig b/arch/arm64/configs/msmcortex_defconfig index dfd658d815fe..741f2e5bc8f6 100644 --- a/arch/arm64/configs/msmcortex_defconfig +++ b/arch/arm64/configs/msmcortex_defconfig @@ -554,6 +554,7 @@ CONFIG_MSM_RPM_LOG=y CONFIG_MSM_RPM_STATS_LOG=y CONFIG_QSEE_IPC_IRQ_BRIDGE=y CONFIG_QCOM_SMCINVOKE=y +CONFIG_QCOM_EARLY_RANDOM=y CONFIG_MEM_SHARE_QMI_SERVICE=y CONFIG_QCOM_BIMC_BWMON=y CONFIG_ARM_MEMLAT_MON=y diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h index 24165784b803..32441df2270e 100644 --- a/arch/arm64/include/asm/mmu_context.h +++ b/arch/arm64/include/asm/mmu_context.h @@ -27,19 +27,24 @@ #include <asm-generic/mm_hooks.h> #include <asm/cputype.h> #include <asm/pgtable.h> +#include <linux/msm_rtb.h> #ifdef CONFIG_PID_IN_CONTEXTIDR static inline void contextidr_thread_switch(struct task_struct *next) { + pid_t pid = task_pid_nr(next); asm( " msr contextidr_el1, %0\n" " isb" : - : "r" (task_pid_nr(next))); + : "r" (pid)); + uncached_logk(LOGK_CTXID, (void *)(u64)pid); + } #else static inline void contextidr_thread_switch(struct task_struct *next) { + uncached_logk(LOGK_CTXID, (void *)(u64)task_pid_nr(next)); } #endif diff --git a/drivers/clk/msm/clock-mmss-cobalt.c b/drivers/clk/msm/clock-mmss-cobalt.c index 873dd40d3a44..9c1cdf967fb1 100644 --- a/drivers/clk/msm/clock-mmss-cobalt.c +++ b/drivers/clk/msm/clock-mmss-cobalt.c @@ -399,6 +399,8 @@ static struct clk_freq_tbl ftbl_cpp_clk_src[] = { static struct clk_freq_tbl ftbl_cpp_clk_src_vq[] = { F_MM( 100000000, mmsscc_gpll0, 6, 0, 0), F_MM( 200000000, mmsscc_gpll0, 3, 0, 0), + F_MM( 384000000, mmpll4_pll_out, 2, 0, 0), + F_MM( 404000000, mmpll0_pll_out, 2, 0, 0), F_MM( 480000000, mmpll7_pll_out, 2, 0, 0), F_MM( 576000000, mmpll10_pll_out, 1, 0, 0), F_MM( 600000000, mmsscc_gpll0, 1, 0, 0), @@ -1112,8 +1114,8 @@ static struct rcg_clk dp_pixel_clk_src = { .parent = &ext_dp_phy_pll_vco.c, .ops = &clk_ops_rcg_dp, .flags = CLKFLAG_NO_RATE_CACHE, - VDD_DIG_FMAX_MAP3(LOWER, 148380, LOW, 296740, - NOMINAL, 593470), + VDD_DIG_FMAX_MAP3(LOWER, 154000000, LOW, 337500000, + NOMINAL, 675000000), CLK_INIT(dp_pixel_clk_src.c), }, }; @@ -2703,7 +2705,6 @@ static void msm_mmsscc_hamster_fixup(void) csi2phytimer_clk_src.c.fmax[VDD_DIG_LOW_L1] = 269333333; mdp_clk_src.c.fmax[VDD_DIG_LOW_L1] = 330000000; - dp_pixel_clk_src.c.fmax[VDD_DIG_LOWER] = 154000000; extpclk_clk_src.c.fmax[VDD_DIG_LOW] = 312500000; extpclk_clk_src.c.fmax[VDD_DIG_LOW_L1] = 375000000; rot_clk_src.c.fmax[VDD_DIG_LOW_L1] = 330000000; @@ -2736,8 +2737,6 @@ static void msm_mmsscc_v2_fixup(void) csi1_clk_src.c.fmax[VDD_DIG_NOMINAL] = 480000000; csi2_clk_src.c.fmax[VDD_DIG_NOMINAL] = 480000000; csi3_clk_src.c.fmax[VDD_DIG_NOMINAL] = 480000000; - - dp_pixel_clk_src.c.fmax[VDD_DIG_LOWER] = 148380000; } int msm_mmsscc_cobalt_probe(struct platform_device *pdev) diff --git a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c index ac4caa48312d..ca5a9ec2f7f9 100644 --- a/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c +++ b/drivers/input/misc/ots_pat9125/pat9125_linux_driver.c @@ -12,6 +12,7 @@ #include <linux/irq.h> #include <linux/of_gpio.h> #include <linux/delay.h> +#include <linux/regulator/consumer.h> #include "pixart_ots.h" struct pixart_pat9125_data { @@ -22,6 +23,8 @@ struct pixart_pat9125_data { bool press_en; bool inverse_x; bool inverse_y; + struct regulator *vdd; + struct regulator *vld; struct pinctrl *pinctrl; struct pinctrl_state *pinctrl_state_active; struct pinctrl_state *pinctrl_state_suspend; @@ -150,7 +153,7 @@ static ssize_t pat9125_test_store(struct device *dev, int reg_data = 0, i; long rd_addr, wr_addr, wr_data; struct pixart_pat9125_data *data = - (struct pixart_pat9125_data *)dev->driver_data; + (struct pixart_pat9125_data *) dev_get_drvdata(dev); struct i2c_client *client = data->client; for (i = 0; i < sizeof(s); i++) @@ -238,6 +241,98 @@ static int pixart_pinctrl_init(struct pixart_pat9125_data *data) return 0; } +static int pat9125_regulator_init(struct pixart_pat9125_data *data) +{ + int err = 0; + struct device *dev = &data->client->dev; + + data->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->vdd)) { + dev_err(dev, "Failed to get regulator vdd %ld\n", + PTR_ERR(data->vdd)); + return PTR_ERR(data->vdd); + } + + data->vld = devm_regulator_get(dev, "vld"); + if (IS_ERR(data->vld)) { + dev_err(dev, "Failed to get regulator vld %ld\n", + PTR_ERR(data->vld)); + return PTR_ERR(data->vld); + } + + err = regulator_set_voltage(data->vdd, VDD_VTG_MIN_UV, VDD_VTG_MAX_UV); + if (err) { + dev_err(dev, "Failed to set voltage for vdd reg %d\n", err); + return err; + } + + err = regulator_set_load(data->vdd, VDD_ACTIVE_LOAD_UA); + if (err < 0) { + dev_err(dev, "Failed to set opt mode for vdd reg %d\n", err); + return err; + } + + err = regulator_set_voltage(data->vld, VLD_VTG_MIN_UV, VLD_VTG_MAX_UV); + if (err) { + dev_err(dev, "Failed to set voltage for vld reg %d\n", err); + return err; + } + + err = regulator_set_load(data->vld, VLD_ACTIVE_LOAD_UA); + if (err < 0) { + dev_err(dev, "Failed to set opt mode for vld reg %d\n", err); + return err; + } + + return 0; +} + +static int pat9125_power_on(struct pixart_pat9125_data *data, bool on) +{ + int err = 0; + struct device *dev = &data->client->dev; + + if (on) { + err = regulator_enable(data->vdd); + if (err) { + dev_err(dev, "Failed to enable vdd reg %d\n", err); + return err; + } + + usleep_range(DELAY_BETWEEN_REG_US, DELAY_BETWEEN_REG_US + 1); + + /* + * Initialize pixart sensor after some delay, when vdd + * regulator is enabled + */ + if (!ots_sensor_init(data->client)) { + err = -ENODEV; + dev_err(dev, "Failed to initialize sensor %d\n", err); + return err; + } + + err = regulator_enable(data->vld); + if (err) { + dev_err(dev, "Failed to enable vld reg %d\n", err); + return err; + } + } else { + err = regulator_disable(data->vld); + if (err) { + dev_err(dev, "Failed to disable vld reg %d\n", err); + return err; + } + + err = regulator_disable(data->vdd); + if (err) { + dev_err(dev, "Failed to disable vdd reg %d\n", err); + return err; + } + } + + return 0; +} + static int pat9125_parse_dt(struct device *dev, struct pixart_pat9125_data *data) { @@ -351,9 +446,16 @@ static int pat9125_i2c_probe(struct i2c_client *client, } } - if (!ots_sensor_init(client)) { - err = -ENODEV; - goto err_sensor_init; + err = pat9125_regulator_init(data); + if (err) { + dev_err(dev, "Failed to init regulator, %d\n", err); + return err; + } + + err = pat9125_power_on(data, true); + if (err) { + dev_err(dev, "Failed to power-on the sensor %d\n", err); + goto err_power_on; } err = devm_request_threaded_irq(dev, client->irq, NULL, pat9125_irq, @@ -374,7 +476,11 @@ static int pat9125_i2c_probe(struct i2c_client *client, err_sysfs_create: err_request_threaded_irq: -err_sensor_init: +err_power_on: + regulator_set_load(data->vdd, 0); + regulator_set_load(data->vld, 0); + if (pat9125_power_on(data, false) < 0) + dev_err(dev, "Failed to disable regulators\n"); if (data->pinctrl) if (pinctrl_select_state(data->pinctrl, data->pinctrl_state_release) < 0) @@ -388,10 +494,14 @@ static int pat9125_i2c_remove(struct i2c_client *client) struct pixart_pat9125_data *data = i2c_get_clientdata(client); struct device *dev = &data->client->dev; + sysfs_remove_group(&(data->input->dev.kobj), &pat9125_attr_grp); if (data->pinctrl) if (pinctrl_select_state(data->pinctrl, data->pinctrl_state_release) < 0) dev_err(dev, "Couldn't set pin to release state\n"); + regulator_set_load(data->vdd, 0); + regulator_set_load(data->vld, 0); + pat9125_power_on(data, false); return 0; } @@ -399,7 +509,7 @@ static int pat9125_suspend(struct device *dev) { int rc; struct pixart_pat9125_data *data = - (struct pixart_pat9125_data *)dev->driver_data; + (struct pixart_pat9125_data *) dev_get_drvdata(dev); disable_irq(data->client->irq); if (data->pinctrl) { @@ -410,6 +520,12 @@ static int pat9125_suspend(struct device *dev) rc); } + rc = pat9125_power_on(data, false); + if (rc) { + dev_err(dev, "Failed to disable regulators %d\n", rc); + return rc; + } + return 0; } @@ -417,7 +533,7 @@ static int pat9125_resume(struct device *dev) { int rc; struct pixart_pat9125_data *data = - (struct pixart_pat9125_data *)dev->driver_data; + (struct pixart_pat9125_data *) dev_get_drvdata(dev); if (data->pinctrl) { rc = pinctrl_select_state(data->pinctrl, @@ -426,9 +542,26 @@ static int pat9125_resume(struct device *dev) dev_err(dev, "Could not set pin to active state %d\n", rc); } + + rc = pat9125_power_on(data, true); + if (rc) { + dev_err(dev, "Failed to power-on the sensor %d\n", rc); + goto err_sensor_init; + } + enable_irq(data->client->irq); return 0; + +err_sensor_init: + if (data->pinctrl) + if (pinctrl_select_state(data->pinctrl, + data->pinctrl_state_suspend) < 0) + dev_err(dev, "Couldn't set pin to suspend state\n"); + if (pat9125_power_on(data, false) < 0) + dev_err(dev, "Failed to disable regulators\n"); + + return rc; } static const struct i2c_device_id pat9125_device_id[] = { diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.c b/drivers/input/misc/ots_pat9125/pixart_ots.c index fa73ffe40985..3d44d068423a 100644 --- a/drivers/input/misc/ots_pat9125/pixart_ots.c +++ b/drivers/input/misc/ots_pat9125/pixart_ots.c @@ -19,7 +19,8 @@ static void ots_write_read(struct i2c_client *client, u8 address, u8 wdata) bool ots_sensor_init(struct i2c_client *client) { - unsigned char sensor_pid = 0, read_id_ok = 0; + u8 sensor_pid = 0; + bool read_id_ok = false; /* * Read sensor_pid in address 0x00 to check if the @@ -28,7 +29,7 @@ bool ots_sensor_init(struct i2c_client *client) sensor_pid = read_data(client, PIXART_PAT9125_PRODUCT_ID1_REG); if (sensor_pid == PIXART_PAT9125_SENSOR_ID) { - read_id_ok = 1; + read_id_ok = true; /* * PAT9125 sensor recommended settings: diff --git a/drivers/input/misc/ots_pat9125/pixart_ots.h b/drivers/input/misc/ots_pat9125/pixart_ots.h index 824d6bafd9bf..5320d588d341 100644 --- a/drivers/input/misc/ots_pat9125/pixart_ots.h +++ b/drivers/input/misc/ots_pat9125/pixart_ots.h @@ -13,6 +13,13 @@ #define PINCTRL_STATE_ACTIVE "pmx_rot_switch_active" #define PINCTRL_STATE_SUSPEND "pmx_rot_switch_suspend" #define PINCTRL_STATE_RELEASE "pmx_rot_switch_release" +#define VDD_VTG_MIN_UV 1800000 +#define VDD_VTG_MAX_UV 1800000 +#define VDD_ACTIVE_LOAD_UA 10000 +#define VLD_VTG_MIN_UV 2800000 +#define VLD_VTG_MAX_UV 3300000 +#define VLD_ACTIVE_LOAD_UA 10000 +#define DELAY_BETWEEN_REG_US 20000 /* Register addresses */ #define PIXART_PAT9125_PRODUCT_ID1_REG 0x00 diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c index a2aa2983b056..98b29cc3a9e3 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp40.c @@ -1943,7 +1943,7 @@ static void msm_vfe40_stats_cfg_wm_reg( stats_idx = STATS_IDX(stream_info->stream_handle[vfe_idx]); stats_base = VFE40_STATS_BASE(stats_idx); /*WR_ADDR_CFG*/ - msm_camera_io_w(stream_info->framedrop_period << 2, + msm_camera_io_w((stream_info->framedrop_period - 1) << 2, vfe_dev->vfe_base + stats_base + 0x8); /*WR_IRQ_FRAMEDROP_PATTERN*/ msm_camera_io_w(stream_info->framedrop_pattern, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c index c77eff66ccca..0e14e0957a2a 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp44.c @@ -1590,7 +1590,7 @@ static void msm_vfe44_stats_cfg_wm_reg( if (stats_idx == STATS_IDX_BF_SCALE) return; /*WR_ADDR_CFG*/ - msm_camera_io_w(stream_info->framedrop_period << 2, + msm_camera_io_w((stream_info->framedrop_period - 1) << 2, vfe_dev->vfe_base + stats_base + 0x8); /*WR_IRQ_FRAMEDROP_PATTERN*/ msm_camera_io_w(stream_info->framedrop_pattern, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c index 6336892b1b4e..5237b84e9477 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp46.c @@ -1680,7 +1680,7 @@ static void msm_vfe46_stats_cfg_wm_reg( return; /* WR_ADDR_CFG */ - msm_camera_io_w(stream_info->framedrop_period << 2, + msm_camera_io_w((stream_info->framedrop_period - 1) << 2, vfe_dev->vfe_base + stats_base + 0x8); /* WR_IRQ_FRAMEDROP_PATTERN */ msm_camera_io_w(stream_info->framedrop_pattern, diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c index 9a469abc56ca..df7e2a88e7ca 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp47.c @@ -30,10 +30,13 @@ #define CDBG(fmt, args...) pr_debug(fmt, ##args) #define VFE47_8996V1_VERSION 0x70000000 +#define VFE48_SDM660_VERSION 0x80000003 #define VFE47_BURST_LEN 3 +#define VFE48_SDM660_BURST_LEN 4 #define VFE47_FETCH_BURST_LEN 3 #define VFE47_STATS_BURST_LEN 3 +#define VFE48_SDM660_STATS_BURST_LEN 4 #define VFE47_UB_SIZE_VFE0 2048 #define VFE47_UB_SIZE_VFE1 1536 #define VFE47_UB_STATS_SIZE 144 @@ -359,13 +362,13 @@ void msm_vfe47_release_hardware(struct vfe_device *vfe_dev) else id = CAM_AHB_CLIENT_VFE1; + vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(vfe_dev, &rate); + if (cam_config_ahb_clk(NULL, 0, id, CAM_AHB_SUSPEND_VOTE) < 0) pr_err("%s: failed to vote for AHB\n", __func__); vfe_dev->ahb_vote = CAM_AHB_SUSPEND_VOTE; - vfe_dev->hw_info->vfe_ops.platform_ops.set_clk_rate(vfe_dev, &rate); - vfe_dev->hw_info->vfe_ops.platform_ops.enable_clks( vfe_dev, 0); vfe_dev->hw_info->vfe_ops.platform_ops.enable_regulators(vfe_dev, 0); @@ -1504,7 +1507,7 @@ void msm_vfe47_axi_cfg_wm_reg( { uint32_t val; int vfe_idx = msm_isp_get_vfe_idx_for_stream(vfe_dev, stream_info); - uint32_t wm_base; + uint32_t wm_base, burst_len; wm_base = VFE47_WM_BASE(stream_info->wm[vfe_idx][plane_idx]); val = msm_camera_io_r(vfe_dev->vfe_base + wm_base + 0x14); @@ -1522,7 +1525,11 @@ void msm_vfe47_axi_cfg_wm_reg( output_height - 1); msm_camera_io_w(val, vfe_dev->vfe_base + wm_base + 0x1C); /* WR_BUFFER_CFG */ - val = VFE47_BURST_LEN | + if (vfe_dev->vfe_hw_version == VFE48_SDM660_VERSION) + burst_len = VFE48_SDM660_BURST_LEN; + else + burst_len = VFE47_BURST_LEN; + val = burst_len | (stream_info->plane_cfg[vfe_idx][plane_idx]. output_height - 1) << 2 | @@ -2055,7 +2062,7 @@ void msm_vfe47_stats_cfg_wm_reg( stats_base = VFE47_STATS_BASE(stats_idx); /* WR_ADDR_CFG */ - msm_camera_io_w(stream_info->framedrop_period << 2, + msm_camera_io_w((stream_info->framedrop_period - 1) << 2, vfe_dev->vfe_base + stats_base + 0x10); /* WR_IRQ_FRAMEDROP_PATTERN */ msm_camera_io_w(stream_info->framedrop_pattern, @@ -2089,7 +2096,7 @@ void msm_vfe47_stats_clear_wm_reg( void msm_vfe47_stats_cfg_ub(struct vfe_device *vfe_dev) { int i; - uint32_t ub_offset = 0; + uint32_t ub_offset = 0, stats_burst_len; uint32_t ub_size[VFE47_NUM_STATS_TYPE] = { 16, /* MSM_ISP_STATS_HDR_BE */ 16, /* MSM_ISP_STATS_BG */ @@ -2108,9 +2115,14 @@ void msm_vfe47_stats_cfg_ub(struct vfe_device *vfe_dev) else pr_err("%s: incorrect VFE device\n", __func__); + if (vfe_dev->vfe_hw_version == VFE48_SDM660_VERSION) + stats_burst_len = VFE48_SDM660_STATS_BURST_LEN; + else + stats_burst_len = VFE47_STATS_BURST_LEN; + for (i = 0; i < VFE47_NUM_STATS_TYPE; i++) { ub_offset -= ub_size[i]; - msm_camera_io_w(VFE47_STATS_BURST_LEN << 30 | + msm_camera_io_w(stats_burst_len << 30 | ub_offset << 16 | (ub_size[i] - 1), vfe_dev->vfe_base + VFE47_STATS_BASE(i) + 0x14); } diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c index e226f7e40a07..afa498f80928 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_stats_util.c @@ -1263,7 +1263,7 @@ int msm_isp_update_stats_stream(struct vfe_device *vfe_dev, void *arg) stream_info->framedrop_pattern = 0x0; else stream_info->framedrop_pattern = 0x1; - stream_info->framedrop_period = framedrop_period - 1; + stream_info->framedrop_period = framedrop_period; if (stream_info->init_stats_frame_drop == 0) for (k = 0; k < stream_info->num_isp; k++) stream_info->vfe_dev[k]->hw_info-> diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 258e08c1b34f..e0d6977b24a6 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -2748,14 +2748,14 @@ static int msm_cpp_validate_input(unsigned int cmd, void *arg, break; default: { if (ioctl_ptr == NULL) { - pr_err("Wrong ioctl_ptr %pK\n", ioctl_ptr); + pr_err("Wrong ioctl_ptr for cmd %u\n", cmd); return -EINVAL; } *ioctl_ptr = arg; if ((*ioctl_ptr == NULL) || - ((*ioctl_ptr)->ioctl_ptr == NULL)) { - pr_err("Wrong arg %pK\n", arg); + (*ioctl_ptr)->ioctl_ptr == NULL) { + pr_err("Error invalid ioctl argument cmd %u", cmd); return -EINVAL; } break; @@ -2780,6 +2780,12 @@ long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd, pr_err("cpp_dev is null\n"); return -EINVAL; } + + if (_IOC_DIR(cmd) == _IOC_NONE) { + pr_err("Invalid ioctl/subdev cmd %u", cmd); + return -EINVAL; + } + rc = msm_cpp_validate_input(cmd, arg, &ioctl_ptr); if (rc != 0) { pr_err("input validation failed\n"); diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h index 79d7d94582c5..bba0b2bc9cdb 100644 --- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h +++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/include/msm_csiphy_5_0_1_hwreg.h @@ -50,11 +50,11 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_1_3ph = { {0x148, 0xFE}, {0x14C, 0x1}, {0x154, 0x0}, - {0x15C, 0x23}, + {0x15C, 0x63}, {0x160, ULPM_WAKE_UP_TIMER_MODE}, {0x164, 0x00}, - {0x168, 0xA0}, - {0x16C, 0x25}, + {0x168, 0xAC}, + {0x16C, 0xA5}, {0x170, 0x41}, {0x174, 0x41}, {0x178, 0x3E}, @@ -98,7 +98,7 @@ struct csiphy_reg_3ph_parms_t csiphy_v5_0_1_3ph = { {0x0, 0x91}, {0x70C, 0xA5}, {0x38, 0xFE}, - {0x81c, 0x6}, + {0x81c, 0x2}, }; struct csiphy_settings_t csiphy_combo_mode_v5_0_1 = { diff --git a/drivers/platform/msm/ipa/ipa_rm.c b/drivers/platform/msm/ipa/ipa_rm.c index 209264d69b26..bcdd99deae1f 100644 --- a/drivers/platform/msm/ipa/ipa_rm.c +++ b/drivers/platform/msm/ipa/ipa_rm.c @@ -820,7 +820,8 @@ static void ipa_rm_wq_resume_handler(struct work_struct *work) } ipa_rm_resource_consumer_request_work( (struct ipa_rm_resource_cons *)resource, - ipa_rm_work->prev_state, ipa_rm_work->needed_bw, true); + ipa_rm_work->prev_state, ipa_rm_work->needed_bw, true, + ipa_rm_work->inc_usage_count); spin_unlock_irqrestore(&ipa_rm_ctx->ipa_rm_lock, flags); bail: kfree(ipa_rm_work); @@ -916,7 +917,8 @@ int ipa_rm_wq_send_suspend_cmd(enum ipa_rm_resource_name resource_name, int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name, enum ipa_rm_resource_state prev_state, - u32 needed_bw) + u32 needed_bw, + bool inc_usage_count) { int result = -ENOMEM; struct ipa_rm_wq_suspend_resume_work_type *work = kzalloc(sizeof(*work), @@ -926,6 +928,7 @@ int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name, work->resource_name = resource_name; work->prev_state = prev_state; work->needed_bw = needed_bw; + work->inc_usage_count = inc_usage_count; result = queue_work(ipa_rm_ctx->ipa_rm_wq, (struct work_struct *)work); } else { diff --git a/drivers/platform/msm/ipa/ipa_rm_i.h b/drivers/platform/msm/ipa/ipa_rm_i.h index eb86c54d7382..1610bb1e1ead 100644 --- a/drivers/platform/msm/ipa/ipa_rm_i.h +++ b/drivers/platform/msm/ipa/ipa_rm_i.h @@ -118,6 +118,7 @@ struct ipa_rm_wq_suspend_resume_work_type { enum ipa_rm_resource_name resource_name; enum ipa_rm_resource_state prev_state; u32 needed_bw; + bool inc_usage_count; }; @@ -128,7 +129,8 @@ int ipa_rm_wq_send_cmd(enum ipa_rm_wq_cmd wq_cmd, int ipa_rm_wq_send_resume_cmd(enum ipa_rm_resource_name resource_name, enum ipa_rm_resource_state prev_state, - u32 needed_bw); + u32 needed_bw, + bool inc_usage_count); int ipa_rm_wq_send_suspend_cmd(enum ipa_rm_resource_name resource_name, enum ipa_rm_resource_state prev_state, diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.c b/drivers/platform/msm/ipa/ipa_rm_resource.c index 0a3f66307eee..da4490ce0aa0 100644 --- a/drivers/platform/msm/ipa/ipa_rm_resource.c +++ b/drivers/platform/msm/ipa/ipa_rm_resource.c @@ -116,7 +116,8 @@ bail: int ipa_rm_resource_consumer_request_work(struct ipa_rm_resource_cons *consumer, enum ipa_rm_resource_state prev_state, u32 prod_needed_bw, - bool notify_completion) + bool notify_completion, + bool dec_client_on_err) { int driver_result; @@ -135,7 +136,8 @@ int ipa_rm_resource_consumer_request_work(struct ipa_rm_resource_cons *consumer, } else if (driver_result != -EINPROGRESS) { consumer->resource.state = prev_state; consumer->resource.needed_bw -= prod_needed_bw; - consumer->usage_count--; + if (dec_client_on_err) + consumer->usage_count--; } return driver_result; @@ -170,19 +172,22 @@ int ipa_rm_resource_consumer_request( ipa_rm_resource_str(consumer->resource.name)); ipa_rm_wq_send_resume_cmd(consumer->resource.name, prev_state, - prod_needed_bw); + prod_needed_bw, + inc_usage_count); result = -EINPROGRESS; break; } result = ipa_rm_resource_consumer_request_work(consumer, prev_state, prod_needed_bw, - false); + false, + inc_usage_count); break; case IPA_RM_GRANTED: if (wake_client) { result = ipa_rm_resource_consumer_request_work( - consumer, prev_state, prod_needed_bw, false); + consumer, prev_state, prod_needed_bw, false, + inc_usage_count); break; } ipa_rm_perf_profile_change(consumer->resource.name); diff --git a/drivers/platform/msm/ipa/ipa_rm_resource.h b/drivers/platform/msm/ipa/ipa_rm_resource.h index 5c3a0190753f..da149c51c96c 100644 --- a/drivers/platform/msm/ipa/ipa_rm_resource.h +++ b/drivers/platform/msm/ipa/ipa_rm_resource.h @@ -155,7 +155,8 @@ int ipa_rm_resource_producer_print_stat( int ipa_rm_resource_consumer_request_work(struct ipa_rm_resource_cons *consumer, enum ipa_rm_resource_state prev_state, u32 needed_bw, - bool notify_completion); + bool notify_completion, + bool dec_client_on_err); int ipa_rm_resource_consumer_release_work( struct ipa_rm_resource_cons *consumer, diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index 94e8bba1fe01..09c7c1b0fd05 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -778,10 +778,28 @@ static void ipa3_transport_irq_cmd_ack(void *user1, int user2) */ int ipa3_send_cmd(u16 num_desc, struct ipa3_desc *descr) { + return ipa3_send_cmd_timeout(num_desc, descr, 0); +} + +/** + * ipa3_send_cmd_timeout - send immediate commands with limited time + * waiting for ACK from IPA HW + * @num_desc: number of descriptors within the desc struct + * @descr: descriptor structure + * @timeout: millisecond to wait till get ACK from IPA HW + * + * Function will block till command gets ACK from IPA HW or timeout. + * Caller needs to free any resources it allocated after function returns + * The callback in ipa3_desc should not be set by the caller + * for this function. + */ +int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout) +{ struct ipa3_desc *desc; int i, result = 0; struct ipa3_sys_context *sys; int ep_idx; + int completed; for (i = 0; i < num_desc; i++) IPADBG("sending imm cmd %d\n", descr[i].opcode); @@ -808,7 +826,14 @@ int ipa3_send_cmd(u16 num_desc, struct ipa3_desc *descr) result = -EFAULT; goto bail; } - wait_for_completion(&descr->xfer_done); + if (timeout) { + completed = wait_for_completion_timeout( + &descr->xfer_done, msecs_to_jiffies(timeout)); + if (!completed) + IPADBG("timeout waiting for imm-cmd ACK\n"); + } else { + wait_for_completion(&descr->xfer_done); + } } else { desc = &descr[num_desc - 1]; init_completion(&desc->xfer_done); @@ -823,7 +848,15 @@ int ipa3_send_cmd(u16 num_desc, struct ipa3_desc *descr) result = -EFAULT; goto bail; } - wait_for_completion(&desc->xfer_done); + if (timeout) { + completed = wait_for_completion_timeout( + &desc->xfer_done, msecs_to_jiffies(timeout)); + if (!completed) + IPADBG("timeout waiting for imm-cmd ACK\n"); + } else { + wait_for_completion(&desc->xfer_done); + } + } bail: diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c index 11da023c9d6a..93fa1492cfd5 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c @@ -326,7 +326,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, int needed_len; int mem_size; - IPADBG_LOW("processing type %d hdr_hdl %d\n", + IPADBG_LOW("Add processing type %d hdr_hdl %d\n", proc_ctx->type, proc_ctx->hdr_hdl); if (!HDR_PROC_TYPE_IS_VALID(proc_ctx->type)) { @@ -335,10 +335,17 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, } hdr_entry = ipa3_id_find(proc_ctx->hdr_hdl); - if (!hdr_entry || (hdr_entry->cookie != IPA_COOKIE)) { + if (!hdr_entry) { IPAERR("hdr_hdl is invalid\n"); return -EINVAL; } + if (hdr_entry->cookie != IPA_COOKIE) { + IPAERR("Invalid header cookie %u\n", hdr_entry->cookie); + WARN_ON(1); + return -EINVAL; + } + IPADBG("Associated header is name=%s is_hdr_proc_ctx=%d\n", + hdr_entry->name, hdr_entry->is_hdr_proc_ctx); entry = kmem_cache_zalloc(ipa3_ctx->hdr_proc_ctx_cache, GFP_KERNEL); if (!entry) { @@ -403,7 +410,7 @@ static int __ipa_add_hdr_proc_ctx(struct ipa_hdr_proc_ctx_add *proc_ctx, entry->offset_entry = offset; list_add(&entry->link, &htbl->head_proc_ctx_entry_list); htbl->proc_ctx_cnt++; - IPADBG_LOW("add proc ctx of sz=%d cnt=%d ofst=%d\n", needed_len, + IPADBG("add proc ctx of sz=%d cnt=%d ofst=%d\n", needed_len, htbl->proc_ctx_cnt, offset->offset); id = ipa3_id_alloc(entry); @@ -520,12 +527,12 @@ static int __ipa_add_hdr(struct ipa_hdr_add *hdr) list_add(&entry->link, &htbl->head_hdr_entry_list); htbl->hdr_cnt++; if (entry->is_hdr_proc_ctx) - IPADBG_LOW("add hdr of sz=%d hdr_cnt=%d phys_base=%pa\n", + IPADBG("add hdr of sz=%d hdr_cnt=%d phys_base=%pa\n", hdr->hdr_len, htbl->hdr_cnt, &entry->phys_base); else - IPADBG_LOW("add hdr of sz=%d hdr_cnt=%d ofst=%d\n", + IPADBG("add hdr of sz=%d hdr_cnt=%d ofst=%d\n", hdr->hdr_len, htbl->hdr_cnt, entry->offset_entry->offset); @@ -580,7 +587,7 @@ static int __ipa3_del_hdr_proc_ctx(u32 proc_ctx_hdl, bool release_hdr) return -EINVAL; } - IPADBG("del ctx proc cnt=%d ofst=%d\n", + IPADBG("del proc ctx cnt=%d ofst=%d\n", htbl->proc_ctx_cnt, entry->offset_entry->offset); if (--entry->ref_cnt) { @@ -624,11 +631,12 @@ int __ipa3_del_hdr(u32 hdr_hdl) } if (entry->is_hdr_proc_ctx) - IPADBG("del hdr of sz=%d hdr_cnt=%d phys_base=%pa\n", + IPADBG("del hdr of len=%d hdr_cnt=%d phys_base=%pa\n", entry->hdr_len, htbl->hdr_cnt, &entry->phys_base); else - IPADBG("del hdr of sz=%d hdr_cnt=%d ofst=%d\n", entry->hdr_len, - htbl->hdr_cnt, entry->offset_entry->offset); + IPADBG("del hdr of len=%d hdr_cnt=%d ofst=%d\n", + entry->hdr_len, htbl->hdr_cnt, + entry->offset_entry->offset); if (--entry->ref_cnt) { IPADBG("hdr_hdl %x ref_cnt %d\n", hdr_hdl, entry->ref_cnt); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index 8e85822d9719..33be22f98b9d 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1835,6 +1835,7 @@ int ipa3_init_mem_partition(struct device_node *dev_node); int ipa3_controller_static_bind(struct ipa3_controller *controller, enum ipa_hw_type ipa_hw_type); int ipa3_cfg_route(struct ipahal_reg_route *route); +int ipa3_send_cmd_timeout(u16 num_desc, struct ipa3_desc *descr, u32 timeout); int ipa3_send_cmd(u16 num_desc, struct ipa3_desc *descr); int ipa3_cfg_filter(u32 disable); int ipa3_pipe_mem_init(u32 start_ofst, u32 size); diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c index 6c7bf500e760..ac7e57f10062 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c @@ -1210,8 +1210,9 @@ int __ipa3_del_rt_rule(u32 rule_hdl) __ipa3_release_hdr_proc_ctx(entry->proc_ctx->id); list_del(&entry->link); entry->tbl->rule_cnt--; - IPADBG("del rt rule tbl_idx=%d rule_cnt=%d rule_id=%d\n", - entry->tbl->idx, entry->tbl->rule_cnt, entry->rule_id); + IPADBG("del rt rule tbl_idx=%d rule_cnt=%d rule_id=%d\n ref_cnt=%u", + entry->tbl->idx, entry->tbl->rule_cnt, + entry->rule_id, entry->tbl->ref_cnt); idr_remove(&entry->tbl->rule_ids, entry->rule_id); if (entry->tbl->rule_cnt == 0 && entry->tbl->ref_cnt == 0) { if (__ipa_del_rt_tbl(entry->tbl)) @@ -1488,6 +1489,8 @@ int ipa3_put_rt_tbl(u32 rt_tbl_hdl) entry->ref_cnt--; if (entry->ref_cnt == 0 && entry->rule_cnt == 0) { + IPADBG("zero ref_cnt, delete rt tbl (idx=%u)\n", + entry->idx); if (__ipa_del_rt_tbl(entry)) IPAERR("fail to del RT tbl\n"); /* commit for put */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c index a21eb9c1530b..c0a6e8b00d71 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c @@ -47,6 +47,8 @@ #define IPA_EOT_COAL_GRAN_MIN (1) #define IPA_EOT_COAL_GRAN_MAX (16) +#define IPA_DMA_TASK_FOR_GSI_TIMEOUT_MSEC (15) + #define IPA_AGGR_BYTE_LIMIT (\ IPA_ENDP_INIT_AGGR_N_AGGR_BYTE_LIMIT_BMSK >> \ IPA_ENDP_INIT_AGGR_N_AGGR_BYTE_LIMIT_SHFT) @@ -101,7 +103,7 @@ #define IPA_GROUP_DPL IPA_GROUP_DL #define IPA_GROUP_DIAG (2) #define IPA_GROUP_DMA (3) -#define IPA_GROUP_IMM_CMD IPA_GROUP_DMA +#define IPA_GROUP_IMM_CMD IPA_GROUP_UL #define IPA_GROUP_Q6ZIP (4) #define IPA_GROUP_Q6ZIP_GENERAL IPA_GROUP_Q6ZIP #define IPA_GROUP_UC_RX_Q (5) @@ -3470,7 +3472,8 @@ int ipa3_inject_dma_task_for_gsi(void) desc.type = IPA_IMM_CMD_DESC; IPADBG("sending 1B packet to IPA\n"); - if (ipa3_send_cmd(1, &desc)) { + if (ipa3_send_cmd_timeout(1, &desc, + IPA_DMA_TASK_FOR_GSI_TIMEOUT_MSEC)) { IPAERR("ipa3_send_cmd failed\n"); return -EFAULT; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c index bcd2cb3bfd7a..2bc179d5a33c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c @@ -1222,9 +1222,9 @@ int ipahal_cp_proc_ctx_to_hw_buff(enum ipa_hdr_proc_type type, if (!base || !hdr_len || - (!phys_base && !hdr_base_addr) || - !hdr_base_addr || - ((is_hdr_proc_ctx == false) && !offset_entry)) { + (is_hdr_proc_ctx && !phys_base) || + (!is_hdr_proc_ctx && !offset_entry) || + (!is_hdr_proc_ctx && !hdr_base_addr)) { 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 diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 0619b314b7de..ea2694c8c58d 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -249,6 +249,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(flash_current_max), POWER_SUPPLY_ATTR(update_now), POWER_SUPPLY_ATTR(esr_count), + POWER_SUPPLY_ATTR(buck_freq), POWER_SUPPLY_ATTR(safety_timer_enabled), POWER_SUPPLY_ATTR(charge_done), POWER_SUPPLY_ATTR(flash_active), @@ -273,6 +274,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(charger_temp), POWER_SUPPLY_ATTR(charger_temp_max), POWER_SUPPLY_ATTR(parallel_disable), + POWER_SUPPLY_ATTR(parallel_percent), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ diff --git a/drivers/power/qcom-charger/bcl_peripheral.c b/drivers/power/qcom-charger/bcl_peripheral.c index fc958b160f86..8a7012ac2bef 100644 --- a/drivers/power/qcom-charger/bcl_peripheral.c +++ b/drivers/power/qcom-charger/bcl_peripheral.c @@ -459,7 +459,7 @@ static void bcl_lmh_dcvs_enable(void) desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL, SCM_VAL, SCM_VAL); - dmac_flush_range(payload, payload + 5 * (sizeof(uint32_t))); + dmac_flush_range(payload, (void *)payload + 5 * (sizeof(uint32_t))); if (scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, LMH_DCVSH), &desc_arg)) pr_err("Error enabling LMH BCL monitoringfor cluster0\n"); diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index 9e5df70541f2..dee554b6e150 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -216,12 +217,15 @@ struct smb_dt_props { u32 step_soc_threshold[STEP_CHARGING_MAX_STEPS - 1]; s32 step_cc_delta[STEP_CHARGING_MAX_STEPS]; struct device_node *revid_dev_node; + int float_option; + bool hvdcp_disable; }; struct smb2 { - struct smb_charger chg; - struct smb_dt_props dt; - bool bad_part; + struct smb_charger chg; + struct dentry *dfs_root; + struct smb_dt_props dt; + bool bad_part; }; static int __debug_mask; @@ -229,11 +233,6 @@ module_param_named( debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR ); -static int __pl_master_percent = 50; -module_param_named( - pl_master_percent, __pl_master_percent, int, S_IRUSR | S_IWUSR -); - #define MICRO_1P5A 1500000 static int smb2_parse_dt(struct smb2 *chip) { @@ -320,6 +319,15 @@ static int smb2_parse_dt(struct smb2 *chip) } } + of_property_read_u32(node, "qcom,float-option", &chip->dt.float_option); + if (chip->dt.float_option < 0 || chip->dt.float_option > 4) { + pr_err("qcom,float-option is out of range [0, 4]\n"); + return -EINVAL; + } + + chip->dt.hvdcp_disable = of_property_read_bool(node, + "qcom,hvdcp-disable"); + return 0; } @@ -333,6 +341,7 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_VOLTAGE_MIN, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_PD_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_TYPE, POWER_SUPPLY_PROP_TYPEC_MODE, @@ -342,7 +351,6 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_PD_ACTIVE, POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, - POWER_SUPPLY_PROP_PARALLEL_DISABLE, }; static int smb2_usb_get_prop(struct power_supply *psy, @@ -372,6 +380,9 @@ static int smb2_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: rc = smblib_get_prop_usb_voltage_now(chg, val); break; + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + rc = smblib_get_prop_pd_current_max(chg, val); + break; case POWER_SUPPLY_PROP_CURRENT_MAX: rc = smblib_get_prop_usb_current_max(chg, val); break; @@ -405,9 +416,11 @@ static int smb2_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW: rc = smblib_get_prop_usb_current_now(chg, val); break; - case POWER_SUPPLY_PROP_PARALLEL_DISABLE: - val->intval = get_client_vote(chg->pl_disable_votable, - USER_VOTER); + case POWER_SUPPLY_PROP_PD_IN_HARD_RESET: + rc = smblib_get_prop_pd_in_hard_reset(chg, val); + break; + case POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED: + val->intval = chg->system_suspend_supported; break; default: pr_err("get prop %d is not supported\n", psp); @@ -436,25 +449,23 @@ static int smb2_usb_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_set_prop_usb_voltage_max(chg, val); break; + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + rc = smblib_set_prop_pd_current_max(chg, val); + break; case POWER_SUPPLY_PROP_CURRENT_MAX: rc = smblib_set_prop_usb_current_max(chg, val); break; - case POWER_SUPPLY_PROP_TYPE: - if (chg->pd_active && val->intval == POWER_SUPPLY_TYPE_USB_PD) { - chg->usb_psy_desc.type = val->intval; - } else { - pr_err("set type %d not allowed\n", val->intval); - rc = -EINVAL; - } - break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: rc = smblib_set_prop_typec_power_role(chg, val); break; case POWER_SUPPLY_PROP_PD_ACTIVE: rc = smblib_set_prop_pd_active(chg, val); break; - case POWER_SUPPLY_PROP_PARALLEL_DISABLE: - vote(chg->pl_disable_votable, USER_VOTER, (bool)val->intval, 0); + case POWER_SUPPLY_PROP_PD_IN_HARD_RESET: + rc = smblib_set_prop_pd_in_hard_reset(chg, val); + break; + case POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED: + chg->system_suspend_supported = val->intval; break; default: pr_err("set prop %d is not supported\n", psp); @@ -470,7 +481,6 @@ static int smb2_usb_prop_is_writeable(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: - case POWER_SUPPLY_PROP_PARALLEL_DISABLE: return 1; default: break; @@ -629,6 +639,7 @@ static enum power_supply_property smb2_batt_props[] = { POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, POWER_SUPPLY_PROP_STEP_CHARGING_STEP, POWER_SUPPLY_PROP_CHARGE_DONE, + POWER_SUPPLY_PROP_PARALLEL_DISABLE, }; static int smb2_batt_get_prop(struct power_supply *psy, @@ -689,6 +700,10 @@ static int smb2_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_DONE: rc = smblib_get_prop_batt_charge_done(chg, val); break; + case POWER_SUPPLY_PROP_PARALLEL_DISABLE: + val->intval = get_client_vote(chg->pl_disable_votable, + USER_VOTER); + break; default: pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; @@ -719,6 +734,9 @@ static int smb2_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: rc = smblib_set_prop_batt_capacity(chg, val); break; + case POWER_SUPPLY_PROP_PARALLEL_DISABLE: + vote(chg->pl_disable_votable, USER_VOTER, (bool)val->intval, 0); + break; default: rc = -EINVAL; } @@ -733,6 +751,7 @@ static int smb2_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_SUSPEND: case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_PARALLEL_DISABLE: return 1; default: break; @@ -1022,6 +1041,14 @@ static int smb2_init_hw(struct smb2 *chip) DEFAULT_VOTER, true, chip->dt.usb_icl_ua); vote(chg->dc_icl_votable, DEFAULT_VOTER, true, chip->dt.dc_icl_ua); + vote(chg->hvdcp_disable_votable, DEFAULT_VOTER, + chip->dt.hvdcp_disable, 0); + vote(chg->hvdcp_disable_votable, PD_INACTIVE_VOTER, + true, 0); + vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, + true, 0); + vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, + true, 0); /* Configure charge enable for software control; active high */ rc = smblib_masked_write(chg, CHGR_CFG2_REG, @@ -1119,6 +1146,35 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } + /* configure float charger options */ + switch (chip->dt.float_option) { + case 1: + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + FLOAT_OPTIONS_MASK, 0); + break; + case 2: + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + FLOAT_OPTIONS_MASK, FORCE_FLOAT_SDP_CFG_BIT); + break; + case 3: + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + FLOAT_OPTIONS_MASK, FLOAT_DIS_CHGING_CFG_BIT); + break; + case 4: + rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, + FLOAT_OPTIONS_MASK, SUSPEND_FLOAT_CFG_BIT); + break; + default: + rc = 0; + break; + } + + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure float charger options rc=%d\n", + rc); + return rc; + } + return rc; } @@ -1439,9 +1495,74 @@ static int smb2_request_interrupts(struct smb2 *chip) return rc; } -/********* - * PROBE * - *********/ +#if defined(CONFIG_DEBUG_FS) + +static int force_batt_psy_update_write(void *data, u64 val) +{ + struct smb_charger *chg = data; + + power_supply_changed(chg->batt_psy); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(force_batt_psy_update_ops, NULL, + force_batt_psy_update_write, "0x%02llx\n"); + +static int force_usb_psy_update_write(void *data, u64 val) +{ + struct smb_charger *chg = data; + + power_supply_changed(chg->usb_psy); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(force_usb_psy_update_ops, NULL, + force_usb_psy_update_write, "0x%02llx\n"); + +static int force_dc_psy_update_write(void *data, u64 val) +{ + struct smb_charger *chg = data; + + power_supply_changed(chg->dc_psy); + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(force_dc_psy_update_ops, NULL, + force_dc_psy_update_write, "0x%02llx\n"); + +static void smb2_create_debugfs(struct smb2 *chip) +{ + struct dentry *file; + + chip->dfs_root = debugfs_create_dir("charger", NULL); + if (IS_ERR_OR_NULL(chip->dfs_root)) { + pr_err("Couldn't create charger debugfs rc=%ld\n", + (long)chip->dfs_root); + return; + } + + file = debugfs_create_file("force_batt_psy_update", S_IRUSR | S_IWUSR, + chip->dfs_root, chip, &force_batt_psy_update_ops); + if (IS_ERR_OR_NULL(file)) + pr_err("Couldn't create force_batt_psy_update file rc=%ld\n", + (long)file); + + file = debugfs_create_file("force_usb_psy_update", S_IRUSR | S_IWUSR, + chip->dfs_root, chip, &force_usb_psy_update_ops); + if (IS_ERR_OR_NULL(file)) + pr_err("Couldn't create force_usb_psy_update file rc=%ld\n", + (long)file); + + file = debugfs_create_file("force_dc_psy_update", S_IRUSR | S_IWUSR, + chip->dfs_root, chip, &force_dc_psy_update_ops); + if (IS_ERR_OR_NULL(file)) + pr_err("Couldn't create force_dc_psy_update file rc=%ld\n", + (long)file); +} + +#else + +static void smb2_create_debugfs(struct smb2 *chip) +{} + +#endif static int smb2_probe(struct platform_device *pdev) { @@ -1459,7 +1580,7 @@ static int smb2_probe(struct platform_device *pdev) chg->param = v1_params; chg->debug_mask = &__debug_mask; chg->mode = PARALLEL_MASTER; - chg->pl.master_percent = &__pl_master_percent; + chg->name = "PMI"; chg->regmap = dev_get_regmap(chg->dev->parent, NULL); if (!chg->regmap) { @@ -1528,6 +1649,7 @@ static int smb2_probe(struct platform_device *pdev) return 0; } + chg->pl.slave_pct = 50; rc = smb2_init_batt_psy(chip); if (rc < 0) { pr_err("Couldn't initialize batt psy rc=%d\n", rc); @@ -1553,6 +1675,8 @@ static int smb2_probe(struct platform_device *pdev) goto cleanup; } + smb2_create_debugfs(chip); + pr_info("QPNP SMB2 probed successfully\n"); return rc; diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index 3a32b8f3d50a..bcdfdd459b3d 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -22,12 +22,18 @@ #include "storm-watch.h" #include "pmic-voter.h" +#define smblib_err(chg, fmt, ...) \ + pr_err("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__) \ + #define smblib_dbg(chg, reason, fmt, ...) \ do { \ if (*chg->debug_mask & (reason)) \ - dev_info(chg->dev, fmt, ##__VA_ARGS__); \ + pr_info("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ else \ - dev_dbg(chg->dev, fmt, ##__VA_ARGS__); \ + pr_debug("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ } while (0) static bool is_secure(struct smb_charger *chg, int addr) @@ -85,21 +91,19 @@ unlock: return rc; } -static int smblib_get_step_charging_adjustment(struct smb_charger *chg, - int *cc_offset) +static int smblib_get_step_cc_delta(struct smb_charger *chg, int *cc_delta_ua) { - int step_state; - int rc; + int rc, step_state; u8 stat; if (!chg->step_chg_enabled) { - *cc_offset = 0; + *cc_delta_ua = 0; return 0; } rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", rc); return rc; } @@ -107,57 +111,70 @@ static int smblib_get_step_charging_adjustment(struct smb_charger *chg, step_state = (stat & STEP_CHARGING_STATUS_MASK) >> STEP_CHARGING_STATUS_SHIFT; rc = smblib_get_charge_param(chg, &chg->param.step_cc_delta[step_state], - cc_offset); + cc_delta_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't get step cc delta rc=%d\n", rc); + return rc; + } - return rc; + return 0; } -static void smblib_fcc_split_ua(struct smb_charger *chg, int total_fcc, - int *master_ua, int *slave_ua) +static int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua) { - int rc, cc_reduction_ua = 0; - int step_cc_delta; - int master_percent = min(max(*chg->pl.master_percent, 0), 100); - union power_supply_propval pval = {0, }; - int effective_fcc; + int rc, cc_minus_ua; + u8 stat; - /* - * if master_percent is 0, s/w will configure master's fcc to zero and - * slave's fcc to the max. However since master's fcc is zero it - * disables its own charging and as a result the slave's charging is - * disabled via the fault line. - */ + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", + rc); + return rc; + } - rc = smblib_get_prop_batt_health(chg, &pval); - if (rc == 0) { - if (pval.intval == POWER_SUPPLY_HEALTH_WARM - || pval.intval == POWER_SUPPLY_HEALTH_COOL) { - rc = smblib_get_charge_param(chg, - &chg->param.jeita_cc_comp, - &cc_reduction_ua); - if (rc < 0) { - dev_err(chg->dev, "Could not get jeita comp, rc=%d\n", - rc); - cc_reduction_ua = 0; - } - } + if (!(stat & BAT_TEMP_STATUS_SOFT_LIMIT_MASK)) { + *cc_delta_ua = 0; + return 0; } - rc = smblib_get_step_charging_adjustment(chg, &step_cc_delta); - if (rc < 0) - step_cc_delta = 0; + rc = smblib_get_charge_param(chg, &chg->param.jeita_cc_comp, + &cc_minus_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't get jeita cc minus rc=%d\n", rc); + return rc; + } - /* - * During JEITA condition and with step_charging enabled, PMI will - * pick the lower of the two value: (FCC - JEITA current compensation) - * or (FCC + step_charging current delta) - */ + *cc_delta_ua = -cc_minus_ua; + return 0; +} + +static void smblib_split_fcc(struct smb_charger *chg, int total_ua, + int *master_ua, int *slave_ua) +{ + int rc, jeita_cc_delta_ua, step_cc_delta_ua, effective_total_ua, + hw_cc_delta_ua = 0; + + rc = smblib_get_step_cc_delta(chg, &step_cc_delta_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't get step cc delta rc=%d\n", rc); + step_cc_delta_ua = 0; + } else { + hw_cc_delta_ua = step_cc_delta_ua; + } + + rc = smblib_get_jeita_cc_delta(chg, &jeita_cc_delta_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't get jeita cc delta rc=%d\n", rc); + jeita_cc_delta_ua = 0; + } else if (jeita_cc_delta_ua < 0) { + /* HW will take the min between JEITA and step charge */ + hw_cc_delta_ua = min(hw_cc_delta_ua, jeita_cc_delta_ua); + } - effective_fcc = min(max(0, total_fcc - cc_reduction_ua), - max(0, total_fcc + step_cc_delta)); - *master_ua = (effective_fcc * master_percent) / 100; - *slave_ua = (effective_fcc - *master_ua) * chg->pl.taper_percent / 100; - *master_ua = max(0, *master_ua + total_fcc - effective_fcc); + effective_total_ua = max(0, total_ua + hw_cc_delta_ua); + *slave_ua = (effective_total_ua * chg->pl.slave_pct) / 100; + *slave_ua = (*slave_ua * chg->pl.taper_pct) / 100; + *master_ua = max(0, total_ua - *slave_ua); } /******************** @@ -172,7 +189,7 @@ int smblib_get_charge_param(struct smb_charger *chg, rc = smblib_read(chg, param->reg, &val_raw); if (rc < 0) { - dev_err(chg->dev, "%s: Couldn't read from 0x%04x rc=%d\n", + smblib_err(chg, "%s: Couldn't read from 0x%04x rc=%d\n", param->name, param->reg, rc); return rc; } @@ -194,7 +211,7 @@ int smblib_get_usb_suspend(struct smb_charger *chg, int *suspend) rc = smblib_read(chg, USBIN_CMD_IL_REG, &temp); if (rc < 0) { - dev_err(chg->dev, "Couldn't read USBIN_CMD_IL rc=%d\n", rc); + smblib_err(chg, "Couldn't read USBIN_CMD_IL rc=%d\n", rc); return rc; } *suspend = temp & USBIN_SUSPEND_BIT; @@ -208,47 +225,97 @@ struct apsd_result { const enum power_supply_type pst; }; +enum { + UNKNOWN, + SDP, + CDP, + DCP, + OCP, + FLOAT, + HVDCP2, + HVDCP3, + MAX_TYPES +}; + static const struct apsd_result const smblib_apsd_results[] = { - {"UNKNOWN", 0, POWER_SUPPLY_TYPE_UNKNOWN}, - {"SDP", SDP_CHARGER_BIT, POWER_SUPPLY_TYPE_USB}, - {"CDP", CDP_CHARGER_BIT, POWER_SUPPLY_TYPE_USB_CDP}, - {"DCP", DCP_CHARGER_BIT, POWER_SUPPLY_TYPE_USB_DCP}, - {"OCP", OCP_CHARGER_BIT, POWER_SUPPLY_TYPE_USB_DCP}, - {"FLOAT", FLOAT_CHARGER_BIT, POWER_SUPPLY_TYPE_USB_DCP}, - {"HVDCP2", DCP_CHARGER_BIT | QC_2P0_BIT, POWER_SUPPLY_TYPE_USB_HVDCP}, - {"HVDCP3", DCP_CHARGER_BIT | QC_3P0_BIT, POWER_SUPPLY_TYPE_USB_HVDCP_3}, + [UNKNOWN] = { + .name = "UNKNOWN", + .bit = 0, + .pst = POWER_SUPPLY_TYPE_UNKNOWN + }, + [SDP] = { + .name = "SDP", + .bit = SDP_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB + }, + [CDP] = { + .name = "CDP", + .bit = CDP_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB_CDP + }, + [DCP] = { + .name = "DCP", + .bit = DCP_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB_DCP + }, + [OCP] = { + .name = "OCP", + .bit = OCP_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB_DCP + }, + [FLOAT] = { + .name = "FLOAT", + .bit = FLOAT_CHARGER_BIT, + .pst = POWER_SUPPLY_TYPE_USB_DCP + }, + [HVDCP2] = { + .name = "HVDCP2", + .bit = DCP_CHARGER_BIT | QC_2P0_BIT, + .pst = POWER_SUPPLY_TYPE_USB_HVDCP + }, + [HVDCP3] = { + .name = "HVDCP3", + .bit = DCP_CHARGER_BIT | QC_3P0_BIT, + .pst = POWER_SUPPLY_TYPE_USB_HVDCP_3, + }, }; static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) { int rc, i; - u8 stat; + u8 apsd_stat, stat; + const struct apsd_result *result = &smblib_apsd_results[UNKNOWN]; - rc = smblib_read(chg, APSD_STATUS_REG, &stat); + rc = smblib_read(chg, APSD_STATUS_REG, &apsd_stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read APSD_STATUS rc=%d\n", rc); - return &smblib_apsd_results[0]; + smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc); + return result; } - smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat); + smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", apsd_stat); - if (!(stat & APSD_DTC_STATUS_DONE_BIT)) - return &smblib_apsd_results[0]; + if (!(apsd_stat & APSD_DTC_STATUS_DONE_BIT)) + return result; rc = smblib_read(chg, APSD_RESULT_STATUS_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read APSD_RESULT_STATUS rc=%d\n", + smblib_err(chg, "Couldn't read APSD_RESULT_STATUS rc=%d\n", rc); - return &smblib_apsd_results[0]; + return result; } stat &= APSD_RESULT_STATUS_MASK; for (i = 0; i < ARRAY_SIZE(smblib_apsd_results); i++) { if (smblib_apsd_results[i].bit == stat) - return &smblib_apsd_results[i]; + result = &smblib_apsd_results[i]; + } + + if (apsd_stat & QC_CHARGER_BIT) { + /* since its a qc_charger, either return HVDCP3 or HVDCP2 */ + if (result != &smblib_apsd_results[HVDCP3]) + result = &smblib_apsd_results[HVDCP2]; } - dev_err(chg->dev, "Couldn't find an APSD result for 0x%02x\n", stat); - return &smblib_apsd_results[0]; + return result; } @@ -268,7 +335,7 @@ int smblib_set_charge_param(struct smb_charger *chg, return -EINVAL; } else { if (val_u > param->max_u || val_u < param->min_u) { - dev_err(chg->dev, "%s: %d is out of range [%d, %d]\n", + smblib_err(chg, "%s: %d is out of range [%d, %d]\n", param->name, val_u, param->min_u, param->max_u); return -EINVAL; } @@ -278,7 +345,7 @@ int smblib_set_charge_param(struct smb_charger *chg, rc = smblib_write(chg, param->reg, val_raw); if (rc < 0) { - dev_err(chg->dev, "%s: Couldn't write 0x%02x to 0x%04x rc=%d\n", + smblib_err(chg, "%s: Couldn't write 0x%02x to 0x%04x rc=%d\n", param->name, val_raw, param->reg, rc); return rc; } @@ -295,14 +362,14 @@ static int step_charge_soc_update(struct smb_charger *chg, int capacity) rc = smblib_set_charge_param(chg, &chg->param.step_soc, capacity); if (rc < 0) { - dev_err(chg->dev, "Error in updating soc, rc=%d\n", rc); + smblib_err(chg, "Error in updating soc, rc=%d\n", rc); return rc; } rc = smblib_write(chg, STEP_CHG_SOC_VBATT_V_UPDATE_REG, STEP_CHG_SOC_VBATT_V_UPDATE_BIT); if (rc < 0) { - dev_err(chg->dev, + smblib_err(chg, "Couldn't set STEP_CHG_SOC_VBATT_V_UPDATE_REG rc=%d\n", rc); return rc; @@ -318,7 +385,7 @@ int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) rc = smblib_masked_write(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0); if (rc < 0) - dev_err(chg->dev, "Couldn't write %s to USBIN_SUSPEND_BIT rc=%d\n", + smblib_err(chg, "Couldn't write %s to USBIN_SUSPEND_BIT rc=%d\n", suspend ? "suspend" : "resume", rc); return rc; @@ -331,7 +398,7 @@ int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend) rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, DCIN_SUSPEND_BIT, suspend ? DCIN_SUSPEND_BIT : 0); if (rc < 0) - dev_err(chg->dev, "Couldn't write %s to DCIN_SUSPEND_BIT rc=%d\n", + smblib_err(chg, "Couldn't write %s to DCIN_SUSPEND_BIT rc=%d\n", suspend ? "suspend" : "resume", rc); return rc; @@ -359,14 +426,14 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, } else if (min_allowed_uv < MICRO_12V && max_allowed_uv <= MICRO_12V) { allowed_voltage = USBIN_ADAPTER_ALLOW_9V_TO_12V; } else { - dev_err(chg->dev, "invalid allowed voltage [%d, %d]\n", + smblib_err(chg, "invalid allowed voltage [%d, %d]\n", min_allowed_uv, max_allowed_uv); return -EINVAL; } rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage); if (rc < 0) { - dev_err(chg->dev, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n", + smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n", allowed_voltage, rc); return rc; } @@ -378,49 +445,44 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, * HELPER FUNCTIONS * ********************/ -static int smblib_update_usb_type(struct smb_charger *chg) +static int try_rerun_apsd_for_hvdcp(struct smb_charger *chg) { - int rc = 0; const struct apsd_result *apsd_result; - /* if PD is active, APSD is disabled so won't have a valid result */ - if (chg->pd_active) - return rc; - - apsd_result = smblib_get_apsd_result(chg); - chg->usb_psy_desc.type = apsd_result->pst; - return rc; + /* + * PD_INACTIVE_VOTER on hvdcp_disable_votable indicates whether + * apsd rerun was tried earlier + */ + if (get_client_vote(chg->hvdcp_disable_votable, PD_INACTIVE_VOTER)) { + vote(chg->hvdcp_disable_votable, PD_INACTIVE_VOTER, false, 0); + /* ensure hvdcp is enabled */ + if (!get_effective_result(chg->hvdcp_disable_votable)) { + apsd_result = smblib_get_apsd_result(chg); + if (apsd_result->pst == POWER_SUPPLY_TYPE_USB_HVDCP) { + /* rerun APSD */ + smblib_dbg(chg, PR_MISC, "rerun APSD\n"); + smblib_masked_write(chg, CMD_APSD_REG, + APSD_RERUN_BIT, + APSD_RERUN_BIT); + } + } + } + return 0; } -static int smblib_detach_usb(struct smb_charger *chg) +static int smblib_update_usb_type(struct smb_charger *chg) { - int rc; - - cancel_delayed_work_sync(&chg->hvdcp_detect_work); - chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; - - /* reconfigure allowed voltage for HVDCP */ - rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, - USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); - if (rc < 0) { - dev_err(chg->dev, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", - rc); - return rc; - } - - chg->voltage_min_uv = MICRO_5V; - chg->voltage_max_uv = MICRO_5V; + int rc = 0; + const struct apsd_result *apsd_result; - /* clear USB ICL vote for PD_VOTER */ - rc = vote(chg->usb_icl_votable, PD_VOTER, false, 0); - if (rc < 0) { - dev_err(chg->dev, "Couldn't vote for USB ICL rc=%d\n", - rc); - return rc; + /* if PD is active, APSD is disabled so won't have a valid result */ + if (chg->pd_active) { + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; + return 0; } - vote(chg->pd_allowed_votable, DEFAULT_VOTER, false, 0); - + apsd_result = smblib_get_apsd_result(chg); + chg->usb_psy_desc.type = apsd_result->pst; return rc; } @@ -452,7 +514,7 @@ static int smblib_register_notifier(struct smb_charger *chg) chg->nb.notifier_call = smblib_notifier_call; rc = power_supply_reg_notifier(&chg->nb); if (rc < 0) { - pr_err("Couldn't register psy notifier rc = %d\n", rc); + smblib_err(chg, "Couldn't register psy notifier rc = %d\n", rc); return rc; } @@ -526,41 +588,39 @@ static int smblib_fcc_max_vote_callback(struct votable *votable, void *data, } static int smblib_fcc_vote_callback(struct votable *votable, void *data, - int fcc_ua, const char *client) + int total_fcc_ua, const char *client) { struct smb_charger *chg = data; - int rc = 0; union power_supply_propval pval = {0, }; - int master_ua = fcc_ua, slave_ua; + int rc, master_fcc_ua = total_fcc_ua, slave_fcc_ua; - if (fcc_ua < 0) { - smblib_dbg(chg, PR_MISC, "No Voter\n"); + if (total_fcc_ua < 0) return 0; - } if (chg->mode == PARALLEL_MASTER && !get_effective_result_locked(chg->pl_disable_votable)) { - smblib_fcc_split_ua(chg, fcc_ua, &master_ua, &slave_ua); + smblib_split_fcc(chg, total_fcc_ua, &master_fcc_ua, + &slave_fcc_ua); /* * parallel charger is not disabled, implying that * chg->pl.psy exists */ - pval.intval = slave_ua; + pval.intval = slave_fcc_ua; rc = power_supply_set_property(chg->pl.psy, POWER_SUPPLY_PROP_CURRENT_MAX, &pval); if (rc < 0) { - dev_err(chg->dev, "Could not set parallel fcc, rc=%d\n", + smblib_err(chg, "Could not set parallel fcc, rc=%d\n", rc); return rc; } - chg->pl.slave_fcc = slave_ua; + chg->pl.slave_fcc_ua = slave_fcc_ua; } - rc = smblib_set_charge_param(chg, &chg->param.fcc, master_ua); + rc = smblib_set_charge_param(chg, &chg->param.fcc, master_fcc_ua); if (rc < 0) { - dev_err(chg->dev, "Error in setting fcc, rc=%d\n", rc); + smblib_err(chg, "Couldn't set master fcc rc=%d\n", rc); return rc; } @@ -582,8 +642,7 @@ static int smblib_fv_vote_callback(struct votable *votable, void *data, rc = smblib_set_charge_param(chg, &chg->param.fv, fv_uv); if (rc < 0) { - dev_err(chg->dev, - "Couldn't set floating voltage rc=%d\n", rc); + smblib_err(chg, "Couldn't set floating voltage rc=%d\n", rc); return rc; } @@ -592,7 +651,7 @@ static int smblib_fv_vote_callback(struct votable *votable, void *data, rc = power_supply_set_property(chg->pl.psy, POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval); if (rc < 0) { - dev_err(chg->dev, + smblib_err(chg, "Couldn't set float on parallel rc=%d\n", rc); return rc; } @@ -601,41 +660,67 @@ static int smblib_fv_vote_callback(struct votable *votable, void *data, return 0; } -#define USBIN_25MA 25000 -#define USBIN_100MA 100000 +#define USBIN_25MA 25000 +#define USBIN_100MA 100000 +#define USBIN_150MA 150000 +#define USBIN_500MA 500000 +#define USBIN_900MA 900000 static int smblib_usb_icl_vote_callback(struct votable *votable, void *data, int icl_ua, const char *client) { struct smb_charger *chg = data; int rc = 0; - bool suspend; + bool suspend = (icl_ua < USBIN_25MA); + u8 icl_options = 0; - if (icl_ua < 0) { - smblib_dbg(chg, PR_MISC, "No Voter hence suspending\n"); - icl_ua = 0; - } - - suspend = (icl_ua < USBIN_25MA); if (suspend) - goto suspend; + goto out; - if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB) - rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, - USB51_MODE_BIT, - (icl_ua > USBIN_100MA) ? USB51_MODE_BIT : 0); - else + if (chg->usb_psy_desc.type != POWER_SUPPLY_TYPE_USB) { rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc); + return rc; + } + + goto out; + } + + /* power source is SDP */ + switch (icl_ua) { + case USBIN_100MA: + /* USB 2.0 100mA */ + icl_options = 0; + break; + case USBIN_150MA: + /* USB 3.0 150mA */ + icl_options = CFG_USB3P0_SEL_BIT; + break; + case USBIN_500MA: + /* USB 2.0 500mA */ + icl_options = USB51_MODE_BIT; + break; + case USBIN_900MA: + /* USB 3.0 900mA */ + icl_options = CFG_USB3P0_SEL_BIT | USB51_MODE_BIT; + break; + default: + smblib_err(chg, "ICL %duA isn't supported for SDP\n", icl_ua); + icl_options = 0; + break; + } +out: + rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, + CFG_USB3P0_SEL_BIT | USB51_MODE_BIT, icl_options); if (rc < 0) { - dev_err(chg->dev, - "Couldn't set USB input current limit rc=%d\n", rc); + smblib_err(chg, "Couldn't set ICL opetions rc=%d\n", rc); return rc; } -suspend: rc = vote(chg->usb_suspend_votable, PD_VOTER, suspend, 0); if (rc < 0) { - dev_err(chg->dev, "Couldn't %s input rc=%d\n", + smblib_err(chg, "Couldn't %s input rc=%d\n", suspend ? "suspend" : "resume", rc); return rc; } @@ -650,8 +735,7 @@ static int smblib_otg_cl_config(struct smb_charger *chg, int otg_cl_ua) rc = smblib_set_charge_param(chg, &chg->param.otg_cl, otg_cl_ua); if (rc < 0) { - dev_err(chg->dev, "Couldn't set otg current limit rc=%d\n", - rc); + smblib_err(chg, "Couldn't set otg current limit rc=%d\n", rc); return rc; } @@ -660,7 +744,7 @@ static int smblib_otg_cl_config(struct smb_charger *chg, int otg_cl_ua) ENG_SSUPPLY_CFG_SKIP_TH_V0P2_BIT, otg_cl_ua > MICRO_250MA ? 1 : 0); if (rc < 0) { - dev_err(chg->dev, + smblib_err(chg, "Couldn't write DC_ENG_SSUPPLY_CFG3_REG rc=%d\n", rc); return rc; } @@ -686,7 +770,7 @@ static int smblib_dc_icl_vote_callback(struct votable *votable, void *data, rc = smblib_set_charge_param(chg, &chg->param.dc_icl, icl_ua); if (rc < 0) { - dev_err(chg->dev, "Couldn't set DC input current limit rc=%d\n", + smblib_err(chg, "Couldn't set DC input current limit rc=%d\n", rc); return rc; } @@ -694,13 +778,25 @@ static int smblib_dc_icl_vote_callback(struct votable *votable, void *data, suspend: rc = vote(chg->dc_suspend_votable, USER_VOTER, suspend, 0); if (rc < 0) { - dev_err(chg->dev, "Couldn't vote to %s DC rc=%d\n", + smblib_err(chg, "Couldn't vote to %s DC rc=%d\n", suspend ? "suspend" : "resume", rc); return rc; } return rc; } +static int smblib_pd_disallowed_votable_indirect_callback( + struct votable *votable, void *data, int disallowed, const char *client) +{ + struct smb_charger *chg = data; + int rc; + + rc = vote(chg->pd_allowed_votable, PD_DISALLOWED_INDIRECT_VOTER, + !disallowed, 0); + + return rc; +} + static int smblib_awake_vote_callback(struct votable *votable, void *data, int awake, const char *client) { @@ -724,7 +820,7 @@ static int smblib_pl_disable_vote_callback(struct votable *votable, void *data, if (chg->mode != PARALLEL_MASTER || !chg->pl.psy) return 0; - chg->pl.taper_percent = 100; + chg->pl.taper_pct = 100; rerun_election(chg->fv_votable); rerun_election(chg->fcc_votable); @@ -732,7 +828,7 @@ static int smblib_pl_disable_vote_callback(struct votable *votable, void *data, rc = power_supply_set_property(chg->pl.psy, POWER_SUPPLY_PROP_INPUT_SUSPEND, &pval); if (rc < 0) { - dev_err(chg->dev, + smblib_err(chg, "Couldn't change slave suspend state rc=%d\n", rc); return rc; } @@ -750,7 +846,7 @@ static int smblib_chg_disable_vote_callback(struct votable *votable, void *data, CHARGING_ENABLE_CMD_BIT, chg_disable ? 0 : CHARGING_ENABLE_CMD_BIT); if (rc < 0) { - dev_err(chg->dev, "Couldn't %s charging rc=%d\n", + smblib_err(chg, "Couldn't %s charging rc=%d\n", chg_disable ? "disable" : "enable", rc); return rc; } @@ -768,6 +864,55 @@ static int smblib_pl_enable_indirect_vote_callback(struct votable *votable, return 0; } +static int smblib_hvdcp_disable_vote_callback(struct votable *votable, + void *data, + int hvdcp_disable, const char *client) +{ + struct smb_charger *chg = data; + int rc; + u8 val = HVDCP_AUTH_ALG_EN_CFG_BIT + | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT | HVDCP_EN_BIT; + + /* + * Disable the autonomous bit and auth bit for disabling hvdcp. + * This ensures only qc 2.0 detection runs but no vbus + * negotiation happens. + */ + if (hvdcp_disable) + val = HVDCP_EN_BIT; + + rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, + HVDCP_EN_BIT + | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT + | HVDCP_AUTH_ALG_EN_CFG_BIT, + val); + if (rc < 0) { + smblib_err(chg, "Couldn't %s hvdcp rc=%d\n", + hvdcp_disable ? "disable" : "enable", rc); + return rc; + } + + return 0; +} + +static int smblib_apsd_disable_vote_callback(struct votable *votable, + void *data, + int apsd_disable, const char *client) +{ + struct smb_charger *chg = data; + int rc; + + rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, + AUTO_SRC_DETECT_BIT, + apsd_disable ? 0 : AUTO_SRC_DETECT_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't %s APSD rc=%d\n", + apsd_disable ? "disable" : "enable", rc); + return rc; + } + + return 0; +} /***************** * OTG REGULATOR * *****************/ @@ -783,21 +928,21 @@ int smblib_vbus_regulator_enable(struct regulator_dev *rdev) ENG_BUCKBOOST_HALT1_8_MODE_BIT, ENG_BUCKBOOST_HALT1_8_MODE_BIT); if (rc < 0) { - dev_err(chg->dev, "Couldn't set OTG_ENG_OTG_CFG_REG rc=%d\n", + smblib_err(chg, "Couldn't set OTG_ENG_OTG_CFG_REG rc=%d\n", rc); return rc; } rc = smblib_write(chg, CMD_OTG_REG, OTG_EN_BIT); if (rc < 0) { - dev_err(chg->dev, "Couldn't enable OTG regulator rc=%d\n", rc); + smblib_err(chg, "Couldn't enable OTG regulator rc=%d\n", rc); return rc; } msleep(OTG_SOFT_START_DELAY_MS); rc = smblib_read(chg, OTG_STATUS_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read OTG_STATUS_REG rc=%d\n", rc); + smblib_err(chg, "Couldn't read OTG_STATUS_REG rc=%d\n", rc); return rc; } if (stat & BOOST_SOFTSTART_DONE_BIT) @@ -813,7 +958,7 @@ int smblib_vbus_regulator_disable(struct regulator_dev *rdev) rc = smblib_write(chg, CMD_OTG_REG, 0); if (rc < 0) { - dev_err(chg->dev, "Couldn't disable OTG regulator rc=%d\n", rc); + smblib_err(chg, "Couldn't disable OTG regulator rc=%d\n", rc); return rc; } @@ -822,7 +967,7 @@ int smblib_vbus_regulator_disable(struct regulator_dev *rdev) rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG, ENG_BUCKBOOST_HALT1_8_MODE_BIT, 0); if (rc < 0) { - dev_err(chg->dev, "Couldn't set OTG_ENG_OTG_CFG_REG rc=%d\n", + smblib_err(chg, "Couldn't set OTG_ENG_OTG_CFG_REG rc=%d\n", rc); return rc; } @@ -839,7 +984,7 @@ int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev) rc = smblib_read(chg, CMD_OTG_REG, &cmd); if (rc < 0) { - dev_err(chg->dev, "Couldn't read CMD_OTG rc=%d", rc); + smblib_err(chg, "Couldn't read CMD_OTG rc=%d", rc); return rc; } @@ -862,7 +1007,7 @@ int smblib_vconn_regulator_enable(struct regulator_dev *rdev) */ rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); return rc; } stat = stat & CC_ORIENTATION_BIT ? 0 : VCONN_EN_ORIENTATION_BIT; @@ -870,7 +1015,7 @@ int smblib_vconn_regulator_enable(struct regulator_dev *rdev) VCONN_EN_VALUE_BIT | VCONN_EN_ORIENTATION_BIT, VCONN_EN_VALUE_BIT | stat); if (rc < 0) - dev_err(chg->dev, "Couldn't enable vconn setting rc=%d\n", rc); + smblib_err(chg, "Couldn't enable vconn setting rc=%d\n", rc); return rc; } @@ -883,7 +1028,7 @@ int smblib_vconn_regulator_disable(struct regulator_dev *rdev) rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, VCONN_EN_VALUE_BIT, 0); if (rc < 0) - dev_err(chg->dev, "Couldn't disable vconn regulator rc=%d\n", + smblib_err(chg, "Couldn't disable vconn regulator rc=%d\n", rc); return rc; @@ -897,7 +1042,7 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev) rc = smblib_read(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, &cmd); if (rc < 0) { - dev_err(chg->dev, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n", + smblib_err(chg, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n", rc); return rc; } @@ -925,8 +1070,7 @@ int smblib_get_prop_batt_present(struct smb_charger *chg, rc = smblib_read(chg, BATIF_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read BATIF_INT_RT_STS rc=%d\n", - rc); + smblib_err(chg, "Couldn't read BATIF_INT_RT_STS rc=%d\n", rc); return rc; } @@ -962,7 +1106,7 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, rc = smblib_get_prop_usb_online(chg, &pval); if (rc < 0) { - dev_err(chg->dev, "Couldn't get usb online property rc=%d\n", + smblib_err(chg, "Couldn't get usb online property rc=%d\n", rc); return rc; } @@ -970,7 +1114,7 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, rc = smblib_get_prop_dc_online(chg, &pval); if (rc < 0) { - dev_err(chg->dev, "Couldn't get dc online property rc=%d\n", + smblib_err(chg, "Couldn't get dc online property rc=%d\n", rc); return rc; } @@ -983,7 +1127,7 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", rc); return rc; } @@ -1020,7 +1164,7 @@ int smblib_get_prop_batt_charge_type(struct smb_charger *chg, rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", rc); return rc; } @@ -1052,7 +1196,7 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", rc); return rc; } @@ -1060,7 +1204,7 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, stat); if (stat & CHARGER_ERROR_STATUS_BAT_OV_BIT) { - dev_err(chg->dev, "battery over-voltage\n"); + smblib_err(chg, "battery over-voltage\n"); val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; goto done; } @@ -1095,7 +1239,7 @@ int smblib_get_prop_input_current_limited(struct smb_charger *chg, rc = smblib_read(chg, AICL_STATUS_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read AICL_STATUS rc=%d\n", rc); + smblib_err(chg, "Couldn't read AICL_STATUS rc=%d\n", rc); return rc; } val->intval = (stat & SOFT_ILIMIT_BIT) || chg->is_hdc; @@ -1154,7 +1298,7 @@ int smblib_get_prop_step_chg_step(struct smb_charger *chg, rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", rc); return rc; } @@ -1173,7 +1317,7 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg, rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", rc); return rc; } @@ -1194,14 +1338,14 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg, rc = vote(chg->usb_suspend_votable, USER_VOTER, (bool)val->intval, 0); if (rc < 0) { - dev_err(chg->dev, "Couldn't vote to %s USB rc=%d\n", + smblib_err(chg, "Couldn't vote to %s USB rc=%d\n", (bool)val->intval ? "suspend" : "resume", rc); return rc; } rc = vote(chg->dc_suspend_votable, USER_VOTER, (bool)val->intval, 0); if (rc < 0) { - dev_err(chg->dev, "Couldn't vote to %s DC rc=%d\n", + smblib_err(chg, "Couldn't vote to %s DC rc=%d\n", (bool)val->intval ? "suspend" : "resume", rc); return rc; } @@ -1258,7 +1402,7 @@ int smblib_get_prop_dc_present(struct smb_charger *chg, 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", + smblib_err(chg, "Couldn't read DC_INT_RT_STS_REG rc=%d\n", rc); return rc; } @@ -1283,7 +1427,7 @@ int smblib_get_prop_dc_online(struct smb_charger *chg, 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", + smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", rc); return rc; } @@ -1328,8 +1472,7 @@ int smblib_get_prop_usb_present(struct smb_charger *chg, rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", - rc); + smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); return rc; } smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", @@ -1353,7 +1496,7 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, 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", + smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n", rc); return rc; } @@ -1384,10 +1527,18 @@ int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, return iio_read_channel_processed(chg->iio.usbin_v_chan, &val->intval); } +int smblib_get_prop_pd_current_max(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = get_client_vote_locked(chg->usb_icl_votable, PD_VOTER); + return 0; +} + int smblib_get_prop_usb_current_max(struct smb_charger *chg, union power_supply_propval *val) { - val->intval = get_effective_result_locked(chg->usb_icl_votable); + val->intval = get_client_vote_locked(chg->usb_icl_votable, + USB_PSY_VOTER); return 0; } @@ -1452,8 +1603,7 @@ int smblib_get_prop_typec_cc_orientation(struct smb_charger *chg, rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", - rc); + smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); return rc; } smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", @@ -1487,7 +1637,7 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg) rc = smblib_read(chg, TYPE_C_STATUS_1_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc); + smblib_err(chg, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc); return POWER_SUPPLY_TYPEC_NONE; } smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_1 = 0x%02x\n", stat); @@ -1515,7 +1665,7 @@ static int smblib_get_prop_dfp_mode(struct smb_charger *chg) rc = smblib_read(chg, TYPE_C_STATUS_2_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_2 rc=%d\n", rc); + smblib_err(chg, "Couldn't read TYPE_C_STATUS_2 rc=%d\n", rc); return POWER_SUPPLY_TYPEC_NONE; } smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_2 = 0x%02x\n", stat); @@ -1546,7 +1696,7 @@ int smblib_get_prop_typec_mode(struct smb_charger *chg, rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); val->intval = POWER_SUPPLY_TYPEC_NONE; return rc; } @@ -1573,7 +1723,7 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg, rc = smblib_read(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, &ctrl); if (rc < 0) { - dev_err(chg->dev, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n", + smblib_err(chg, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n", rc); return rc; } @@ -1597,7 +1747,7 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg, break; default: val->intval = POWER_SUPPLY_TYPEC_PR_NONE; - dev_err(chg->dev, "unsupported power role 0x%02lx\n", + smblib_err(chg, "unsupported power role 0x%02lx\n", ctrl & (DFP_EN_CMD_BIT | UFP_EN_CMD_BIT)); return -EINVAL; } @@ -1618,16 +1768,55 @@ int smblib_get_prop_input_current_settled(struct smb_charger *chg, return smblib_get_charge_param(chg, &chg->param.icl_stat, &val->intval); } +int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 ctrl; + + rc = smblib_read(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, &ctrl); + if (rc < 0) { + smblib_err(chg, "Couldn't read TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG rc=%d\n", + rc); + return rc; + } + val->intval = ctrl & EXIT_SNK_BASED_ON_CC_BIT; + return 0; +} + /******************* * USB PSY SETTERS * * *****************/ +int smblib_set_prop_pd_current_max(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + if (chg->pd_active) + rc = vote(chg->usb_icl_votable, PD_VOTER, true, val->intval); + else + rc = -EPERM; + + return rc; +} + int smblib_set_prop_usb_current_max(struct smb_charger *chg, const union power_supply_propval *val) { int rc; - rc = vote(chg->usb_icl_votable, PD_VOTER, true, val->intval); + if (!chg->pd_active) { + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + true, val->intval); + } else if (chg->system_suspend_supported) { + if (val->intval <= USBIN_25MA) + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + true, val->intval); + else + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + false, 0); + } return rc; } @@ -1651,14 +1840,14 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, power_role = DFP_EN_CMD_BIT; break; default: - dev_err(chg->dev, "power role %d not supported\n", val->intval); + smblib_err(chg, "power role %d not supported\n", val->intval); return -EINVAL; } rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_POWER_ROLE_CMD_MASK, power_role); if (rc < 0) { - dev_err(chg->dev, "Couldn't write 0x%02x to TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n", + smblib_err(chg, "Couldn't write 0x%02x to TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n", power_role, rc); return rc; } @@ -1675,7 +1864,7 @@ int smblib_set_prop_usb_voltage_min(struct smb_charger *chg, rc = smblib_set_usb_pd_allowed_voltage(chg, min_uv, chg->voltage_max_uv); if (rc < 0) { - dev_err(chg->dev, "invalid max voltage %duV rc=%d\n", + smblib_err(chg, "invalid max voltage %duV rc=%d\n", val->intval, rc); return rc; } @@ -1697,7 +1886,7 @@ int smblib_set_prop_usb_voltage_max(struct smb_charger *chg, rc = smblib_set_usb_pd_allowed_voltage(chg, chg->voltage_min_uv, max_uv); if (rc < 0) { - dev_err(chg->dev, "invalid min voltage %duV rc=%d\n", + smblib_err(chg, "invalid min voltage %duV rc=%d\n", val->intval, rc); return rc; } @@ -1710,60 +1899,75 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, const union power_supply_propval *val) { int rc; - u8 stat; + u8 stat = 0; + bool cc_debounced; + bool orientation; + bool pd_active = val->intval; if (!get_effective_result(chg->pd_allowed_votable)) { - dev_err(chg->dev, "PD is not allowed\n"); + smblib_err(chg, "PD is not allowed\n"); return -EINVAL; } - rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, - AUTO_SRC_DETECT_BIT, - val->intval ? 0 : AUTO_SRC_DETECT_BIT); - if (rc < 0) { - dev_err(chg->dev, "Couldn't %s APSD rc=%d\n", - val->intval ? "disable" : "enable", rc); - return rc; - } - - vote(chg->pd_allowed_votable, PD_VOTER, val->intval, 0); + vote(chg->apsd_disable_votable, PD_VOTER, pd_active, 0); + vote(chg->pd_allowed_votable, PD_VOTER, pd_active, 0); /* * VCONN_EN_ORIENTATION_BIT controls whether to use CC1 or CC2 line * when TYPEC_SPARE_CFG_BIT (CC pin selection s/w override) is set * or when VCONN_EN_VALUE_BIT is set. */ - if (val->intval) { - rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); - if (rc < 0) { - dev_err(chg->dev, - "Couldn't read TYPE_C_STATUS_4 rc=%d\n", - rc); - return rc; - } + rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); + return rc; + } - stat &= CC_ORIENTATION_BIT; + if (pd_active) { + orientation = stat & CC_ORIENTATION_BIT; rc = smblib_masked_write(chg, - TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - VCONN_EN_ORIENTATION_BIT, - stat ? 0 : VCONN_EN_ORIENTATION_BIT); - if (rc < 0) - dev_err(chg->dev, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + VCONN_EN_ORIENTATION_BIT, + orientation ? 0 : VCONN_EN_ORIENTATION_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't enable vconn on CC line rc=%d\n", rc); + return rc; + } } /* CC pin selection s/w override in PD session; h/w otherwise. */ rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG, TYPEC_SPARE_CFG_BIT, - val->intval ? TYPEC_SPARE_CFG_BIT : 0); + pd_active ? TYPEC_SPARE_CFG_BIT : 0); if (rc < 0) { - dev_err(chg->dev, "Couldn't change cc_out ctrl to %s rc=%d\n", - val->intval ? "SW" : "HW", rc); + smblib_err(chg, "Couldn't change cc_out ctrl to %s rc=%d\n", + pd_active ? "SW" : "HW", rc); return rc; } - chg->pd_active = (bool)val->intval; + cc_debounced = (bool)(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT); + if (!pd_active && cc_debounced) + try_rerun_apsd_for_hvdcp(chg); + + chg->pd_active = pd_active; smblib_update_usb_type(chg); + power_supply_changed(chg->usb_psy); + + return rc; +} + +int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + EXIT_SNK_BASED_ON_CC_BIT, + (val->intval) ? EXIT_SNK_BASED_ON_CC_BIT : 0); + + vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, val->intval, 0); + return rc; } @@ -1836,7 +2040,7 @@ irqreturn_t smblib_handle_chg_state_change(int irq, void *data) rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", rc); return IRQ_HANDLED; } @@ -1891,7 +2095,7 @@ irqreturn_t smblib_handle_step_chg_soc_update_request(int irq, void *data) rc = smblib_get_prop_batt_capacity(chg, &pval); if (rc < 0) - dev_err(chg->dev, "Couldn't get batt capacity rc=%d\n", rc); + smblib_err(chg, "Couldn't get batt capacity rc=%d\n", rc); else step_charge_soc_update(chg, pval.intval); @@ -1940,7 +2144,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) "dpdm-supply", NULL)) { chg->dpdm_reg = devm_regulator_get(chg->dev, "dpdm"); if (IS_ERR(chg->dpdm_reg)) { - dev_err(chg->dev, "Couldn't get dpdm regulator rc=%ld\n", + smblib_err(chg, "Couldn't get dpdm regulator rc=%ld\n", PTR_ERR(chg->dpdm_reg)); chg->dpdm_reg = NULL; } @@ -1951,7 +2155,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); + smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); return IRQ_HANDLED; } @@ -1962,7 +2166,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) smblib_dbg(chg, PR_MISC, "enabling DPDM regulator\n"); rc = regulator_enable(chg->dpdm_reg); if (rc < 0) - dev_err(chg->dev, "Couldn't enable dpdm regulator rc=%d\n", + smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n", rc); } } else { @@ -1970,7 +2174,7 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) smblib_dbg(chg, PR_MISC, "disabling DPDM regulator\n"); rc = regulator_disable(chg->dpdm_reg); if (rc < 0) - dev_err(chg->dev, "Couldn't disable dpdm regulator rc=%d\n", + smblib_err(chg, "Couldn't disable dpdm regulator rc=%d\n", rc); } } @@ -1982,23 +2186,23 @@ skip_dpdm_float: return IRQ_HANDLED; } -#define USB_WEAK_INPUT_MA 1400000 +#define USB_WEAK_INPUT_UA 1400000 irqreturn_t smblib_handle_icl_change(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; - int icl_ma; - int rc; + int rc, icl_ua; - rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &icl_ma); + + rc = smblib_get_charge_param(chg, &chg->param.icl_stat, &icl_ua); if (rc < 0) { - dev_err(chg->dev, "Couldn't get ICL status rc=%d\n", rc); + smblib_err(chg, "Couldn't get ICL status rc=%d\n", rc); return IRQ_HANDLED; } if (chg->mode == PARALLEL_MASTER) vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, - icl_ma >= USB_WEAK_INPUT_MA, 0); + icl_ua >= USB_WEAK_INPUT_UA, 0); smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); @@ -2047,9 +2251,13 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, bool rising, bool qc_charger) { - if (rising && !qc_charger) { - vote(chg->pd_allowed_votable, DEFAULT_VOTER, true, 0); - power_supply_changed(chg->usb_psy); + /* Hold off PD only until hvdcp 2.0 detection timeout */ + if (rising) { + vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, + false, 0); + if (get_effective_result(chg->pd_disallowed_votable_indirect)) + /* could be a legacy cable, try doing hvdcp */ + try_rerun_apsd_for_hvdcp(chg); } smblib_dbg(chg, PR_INTERRUPT, "IRQ: smblib_handle_hvdcp_check_timeout %s\n", @@ -2084,7 +2292,9 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) case CDP_CHARGER_BIT: case OCP_CHARGER_BIT: case FLOAT_CHARGER_BIT: - vote(chg->pd_allowed_votable, DEFAULT_VOTER, true, 0); + /* if not DCP then no hvdcp timeout happens. Enable pd here */ + vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, + false, 0); break; case DCP_CHARGER_BIT: if (chg->wa_flags & QC_CHARGER_DETECTION_WA_BIT) @@ -2097,7 +2307,7 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) rc = smblib_update_usb_type(chg); if (rc < 0) - dev_err(chg->dev, "Couldn't update usb type rc=%d\n", rc); + smblib_err(chg, "Couldn't update usb type rc=%d\n", rc); smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n", apsd_result->name); @@ -2112,7 +2322,7 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) rc = smblib_read(chg, APSD_STATUS_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read APSD_STATUS rc=%d\n", rc); + smblib_err(chg, "Couldn't read APSD_STATUS rc=%d\n", rc); return IRQ_HANDLED; } smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat); @@ -2144,48 +2354,124 @@ irqreturn_t smblib_handle_usb_source_change(int irq, void *data) return IRQ_HANDLED; } -static void smblib_handle_typec_cc(struct smb_charger *chg, bool attached) +static void typec_source_removal(struct smb_charger *chg) { int rc; - if (!attached) { - rc = smblib_detach_usb(chg); - if (rc < 0) - dev_err(chg->dev, "Couldn't detach USB rc=%d\n", rc); + vote(chg->pl_disable_votable, TYPEC_SRC_VOTER, true, 0); + /* reset both usbin current and voltage votes */ + vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); + vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); + /* reset taper_end voter here */ + vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0); + + cancel_delayed_work_sync(&chg->hvdcp_detect_work); + + /* reconfigure allowed voltage for HVDCP */ + rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); + if (rc < 0) + smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V rc=%d\n", + rc); + + chg->voltage_min_uv = MICRO_5V; + chg->voltage_max_uv = MICRO_5V; + + /* clear USB ICL vote for PD_VOTER */ + rc = vote(chg->usb_icl_votable, PD_VOTER, false, 0); + if (rc < 0) + smblib_err(chg, "Couldn't un-vote for USB ICL rc=%d\n", rc); + + /* clear USB ICL vote for USB_PSY_VOTER */ + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); + if (rc < 0) + smblib_err(chg, "Couldn't un-vote for USB ICL rc=%d\n", rc); +} + +static void typec_source_insertion(struct smb_charger *chg) +{ + vote(chg->pl_disable_votable, TYPEC_SRC_VOTER, false, 0); +} + +static void typec_sink_insertion(struct smb_charger *chg) +{ + /* when a sink is inserted we should not wait on hvdcp timeout to + * enable pd + */ + vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, + false, 0); +} + +static void smblib_handle_typec_removal(struct smb_charger *chg) +{ + vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); + vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0); + vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, true, 0); + vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); + + /* reset votes from vbus_cc_short */ + vote(chg->hvdcp_disable_votable, VBUS_CC_SHORT_VOTER, true, 0); + + vote(chg->hvdcp_disable_votable, PD_INACTIVE_VOTER, true, 0); + + /* + * cable could be removed during hard reset, remove its vote to + * disable apsd + */ + vote(chg->apsd_disable_votable, PD_HARD_RESET_VOTER, false, 0); + + typec_source_removal(chg); + + smblib_update_usb_type(chg); +} + +static void smblib_handle_typec_insertion(struct smb_charger *chg, + bool sink_attached, bool legacy_cable) +{ + int rp; + bool vbus_cc_short = false; + + vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, false, 0); + + if (sink_attached) { + typec_source_removal(chg); + typec_sink_insertion(chg); + } else { + typec_source_insertion(chg); } - smblib_dbg(chg, PR_INTERRUPT, "IRQ: CC %s\n", - attached ? "attached" : "detached"); + vote(chg->pd_disallowed_votable_indirect, LEGACY_CABLE_VOTER, + legacy_cable, 0); + + if (legacy_cable) { + rp = smblib_get_prop_ufp_mode(chg); + if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH + || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) { + vbus_cc_short = true; + smblib_err(chg, "Disabling PD and HVDCP, VBUS-CC shorted, rp = %d found\n", + rp); + } + } + + vote(chg->hvdcp_disable_votable, VBUS_CC_SHORT_VOTER, vbus_cc_short, 0); + vote(chg->pd_disallowed_votable_indirect, VBUS_CC_SHORT_VOTER, + vbus_cc_short, 0); } static void smblib_handle_typec_debounce_done(struct smb_charger *chg, - bool rising, bool sink_attached) + bool rising, bool sink_attached, bool legacy_cable) { int rc; union power_supply_propval pval = {0, }; - /* allow PD for attached sinks */ - if (rising && sink_attached) - vote(chg->pd_allowed_votable, DEFAULT_VOTER, true, 0); + if (rising) + smblib_handle_typec_insertion(chg, sink_attached, legacy_cable); + else + smblib_handle_typec_removal(chg); rc = smblib_get_prop_typec_mode(chg, &pval); if (rc < 0) - dev_err(chg->dev, "Couldn't get prop typec mode rc=%d\n", rc); - - /* - * vote to enable parallel charging if a source is attached, and disable - * otherwise - */ - vote(chg->pl_disable_votable, TYPEC_SRC_VOTER, - !rising || sink_attached, 0); - - if (!rising || sink_attached) { - /* reset both usbin current and voltage votes */ - vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); - vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); - /* reset taper_end voter here */ - vote(chg->pl_disable_votable, TAPER_END_VOTER, false, 0); - } + smblib_err(chg, "Couldn't get prop typec mode rc=%d\n", rc); smblib_dbg(chg, PR_INTERRUPT, "IRQ: debounce-done %s; Type-C %s detected\n", rising ? "rising" : "falling", @@ -2198,20 +2484,27 @@ irqreturn_t smblib_handle_usb_typec_change(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; int rc; u8 stat; + bool debounce_done, sink_attached, legacy_cable; rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat); if (rc < 0) { - dev_err(chg->dev, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", - rc); + smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc); return IRQ_HANDLED; } smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_4 = 0x%02x\n", stat); + debounce_done = (bool)(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT); + sink_attached = (bool)(stat & UFP_DFP_MODE_STATUS_BIT); + + rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc); + return IRQ_HANDLED; + } + smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_5 = 0x%02x\n", stat); + legacy_cable = (bool)(stat & TYPEC_LEGACY_CABLE_STATUS_BIT); - smblib_handle_typec_cc(chg, - (bool)(stat & CC_ATTACHED_BIT)); smblib_handle_typec_debounce_done(chg, - (bool)(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT), - (bool)(stat & UFP_DFP_MODE_STATUS_BIT)); + debounce_done, sink_attached, legacy_cable); power_supply_changed(chg->usb_psy); @@ -2250,14 +2543,15 @@ static void smblib_hvdcp_detect_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, hvdcp_detect_work.work); - const struct apsd_result *apsd_result; - apsd_result = smblib_get_apsd_result(chg); - if (apsd_result->bit && - !(apsd_result->bit & (QC_2P0_BIT | QC_3P0_BIT))) { - vote(chg->pd_allowed_votable, DEFAULT_VOTER, true, 0); + vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, + false, 0); + if (get_effective_result(chg->pd_disallowed_votable_indirect)) + /* pd is still disabled, try hvdcp */ + try_rerun_apsd_for_hvdcp(chg); + else + /* notify pd now that pd is allowed */ power_supply_changed(chg->usb_psy); - } } static void bms_update_work(struct work_struct *work) @@ -2276,7 +2570,7 @@ static void step_soc_req_work(struct work_struct *work) rc = smblib_get_prop_batt_capacity(chg, &pval); if (rc < 0) { - dev_err(chg->dev, "Couldn't get batt capacity rc=%d\n", rc); + smblib_err(chg, "Couldn't get batt capacity rc=%d\n", rc); return; } @@ -2293,7 +2587,7 @@ static void smblib_pl_detect_work(struct work_struct *work) #define MINIMUM_PARALLEL_FCC_UA 500000 #define PL_TAPER_WORK_DELAY_MS 100 -#define TAPER_RESIDUAL_PERCENT 75 +#define TAPER_RESIDUAL_PCT 75 static void smblib_pl_taper_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, @@ -2301,22 +2595,22 @@ static void smblib_pl_taper_work(struct work_struct *work) union power_supply_propval pval = {0, }; int rc; - if (chg->pl.slave_fcc < MINIMUM_PARALLEL_FCC_UA) { + if (chg->pl.slave_fcc_ua < MINIMUM_PARALLEL_FCC_UA) { vote(chg->pl_disable_votable, TAPER_END_VOTER, true, 0); goto done; } rc = smblib_get_prop_batt_charge_type(chg, &pval); if (rc < 0) { - dev_err(chg->dev, "Couldn't get batt charge type rc=%d\n", rc); + smblib_err(chg, "Couldn't get batt charge type rc=%d\n", rc); goto done; } if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) { vote(chg->awake_votable, PL_TAPER_WORK_RUNNING_VOTER, true, 0); /* Reduce the taper percent by 25 percent */ - chg->pl.taper_percent = chg->pl.taper_percent - * TAPER_RESIDUAL_PERCENT / 100; + chg->pl.taper_pct = chg->pl.taper_pct + * TAPER_RESIDUAL_PCT / 100; rerun_election(chg->fcc_votable); schedule_delayed_work(&chg->pl_taper_work, msecs_to_jiffies(PL_TAPER_WORK_DELAY_MS)); @@ -2398,8 +2692,16 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } - chg->pd_allowed_votable = create_votable("PD_ALLOWED", VOTE_SET_ANY, - NULL, NULL); + chg->pd_disallowed_votable_indirect + = create_votable("PD_DISALLOWED_INDIRECT", VOTE_SET_ANY, + smblib_pd_disallowed_votable_indirect_callback, chg); + if (IS_ERR(chg->pd_disallowed_votable_indirect)) { + rc = PTR_ERR(chg->pd_disallowed_votable_indirect); + return rc; + } + + chg->pd_allowed_votable = create_votable("PD_ALLOWED", + VOTE_SET_ANY, NULL, NULL); if (IS_ERR(chg->pd_allowed_votable)) { rc = PTR_ERR(chg->pd_allowed_votable); return rc; @@ -2438,6 +2740,24 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } + chg->hvdcp_disable_votable = create_votable("HVDCP_DISABLE", + VOTE_SET_ANY, + smblib_hvdcp_disable_vote_callback, + chg); + if (IS_ERR(chg->hvdcp_disable_votable)) { + rc = PTR_ERR(chg->hvdcp_disable_votable); + return rc; + } + + chg->apsd_disable_votable = create_votable("APSD_DISABLE", + VOTE_SET_ANY, + smblib_apsd_disable_vote_callback, + chg); + if (IS_ERR(chg->apsd_disable_votable)) { + rc = PTR_ERR(chg->apsd_disable_votable); + return rc; + } + return rc; } @@ -2457,6 +2777,8 @@ static void smblib_destroy_votables(struct smb_charger *chg) destroy_votable(chg->usb_icl_votable); if (chg->dc_icl_votable) destroy_votable(chg->dc_icl_votable); + if (chg->pd_disallowed_votable_indirect) + destroy_votable(chg->pd_disallowed_votable_indirect); if (chg->pd_allowed_votable) destroy_votable(chg->pd_allowed_votable); if (chg->awake_votable) @@ -2467,6 +2789,8 @@ static void smblib_destroy_votables(struct smb_charger *chg) destroy_votable(chg->chg_disable_votable); if (chg->pl_enable_votable_indirect) destroy_votable(chg->pl_enable_votable_indirect); + if (chg->apsd_disable_votable) + destroy_votable(chg->apsd_disable_votable); } static void smblib_iio_deinit(struct smb_charger *chg) @@ -2500,14 +2824,14 @@ int smblib_init(struct smb_charger *chg) case PARALLEL_MASTER: rc = smblib_create_votables(chg); if (rc < 0) { - dev_err(chg->dev, "Couldn't create votables rc=%d\n", + smblib_err(chg, "Couldn't create votables rc=%d\n", rc); return rc; } rc = smblib_register_notifier(chg); if (rc < 0) { - dev_err(chg->dev, + smblib_err(chg, "Couldn't register notifier rc=%d\n", rc); return rc; } @@ -2522,7 +2846,7 @@ int smblib_init(struct smb_charger *chg) case PARALLEL_SLAVE: break; default: - dev_err(chg->dev, "Unsupported mode %d\n", chg->mode); + smblib_err(chg, "Unsupported mode %d\n", chg->mode); return -EINVAL; } @@ -2539,7 +2863,7 @@ int smblib_deinit(struct smb_charger *chg) case PARALLEL_SLAVE: break; default: - dev_err(chg->dev, "Unsupported mode %d\n", chg->mode); + smblib_err(chg, "Unsupported mode %d\n", chg->mode); return -EINVAL; } diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index b57114774f8f..9612b740d84f 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -27,6 +27,7 @@ enum print_reason { #define DEFAULT_VOTER "DEFAULT_VOTER" #define USER_VOTER "USER_VOTER" #define PD_VOTER "PD_VOTER" +#define USB_PSY_VOTER "USB_PSY_VOTER" #define PL_TAPER_WORK_RUNNING_VOTER "PL_TAPER_WORK_RUNNING_VOTER" #define PARALLEL_PSY_VOTER "PARALLEL_PSY_VOTER" #define PL_INDIRECT_VOTER "PL_INDIRECT_VOTER" @@ -37,6 +38,13 @@ enum print_reason { #define TAPER_END_VOTER "TAPER_END_VOTER" #define FCC_MAX_RESULT_VOTER "FCC_MAX_RESULT_VOTER" #define THERMAL_DAEMON_VOTER "THERMAL_DAEMON_VOTER" +#define CC_DETACHED_VOTER "CC_DETACHED_VOTER" +#define HVDCP_TIMEOUT_VOTER "HVDCP_TIMEOUT_VOTER" +#define PD_DISALLOWED_INDIRECT_VOTER "PD_DISALLOWED_INDIRECT_VOTER" +#define PD_HARD_RESET_VOTER "PD_HARD_RESET_VOTER" +#define VBUS_CC_SHORT_VOTER "VBUS_CC_SHORT_VOTER" +#define LEGACY_CABLE_VOTER "LEGACY_CABLE_VOTER" +#define PD_INACTIVE_VOTER "PD_INACTIVE_VOTER" enum smb_mode { PARALLEL_MASTER = 0, @@ -89,13 +97,14 @@ struct smb_params { struct smb_chg_param step_soc_threshold[4]; struct smb_chg_param step_soc; struct smb_chg_param step_cc_delta[5]; + struct smb_chg_param freq_buck; }; struct parallel_params { struct power_supply *psy; - int *master_percent; - int taper_percent; - int slave_fcc; + int slave_pct; + int taper_pct; + int slave_fcc_ua; }; struct smb_iio { @@ -108,6 +117,7 @@ struct smb_iio { struct smb_charger { struct device *dev; + char *name; struct regmap *regmap; struct smb_params param; struct smb_iio iio; @@ -144,11 +154,14 @@ struct smb_charger { struct votable *fv_votable; struct votable *usb_icl_votable; struct votable *dc_icl_votable; + struct votable *pd_disallowed_votable_indirect; struct votable *pd_allowed_votable; struct votable *awake_votable; struct votable *pl_disable_votable; struct votable *chg_disable_votable; struct votable *pl_enable_votable_indirect; + struct votable *hvdcp_disable_votable; + struct votable *apsd_disable_votable; /* work */ struct work_struct bms_update_work; @@ -162,8 +175,9 @@ struct smb_charger { /* cached status */ int voltage_min_uv; int voltage_max_uv; - bool pd_active; + int pd_active; bool vbus_present; + bool system_suspend_supported; int system_temp_level; int thermal_levels; @@ -276,6 +290,8 @@ int smblib_get_prop_usb_suspend(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_pd_current_max(struct smb_charger *chg, + union power_supply_propval *val); int smblib_get_prop_usb_current_max(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_usb_current_now(struct smb_charger *chg, @@ -290,10 +306,14 @@ int smblib_get_prop_pd_allowed(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_input_current_settled(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_pd_in_hard_reset(struct smb_charger *chg, + union power_supply_propval *val); int smblib_get_prop_charger_temp(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_charger_temp_max(struct smb_charger *chg, union power_supply_propval *val); +int smblib_set_prop_pd_current_max(struct smb_charger *chg, + const union power_supply_propval *val); int smblib_set_prop_usb_current_max(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_usb_voltage_min(struct smb_charger *chg, @@ -304,6 +324,8 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_pd_active(struct smb_charger *chg, const union power_supply_propval *val); +int smblib_set_prop_pd_in_hard_reset(struct smb_charger *chg, + const union power_supply_propval *val); int smblib_get_prop_slave_current_now(struct smb_charger *chg, union power_supply_propval *val); @@ -311,4 +333,3 @@ int smblib_get_prop_slave_current_now(struct smb_charger *chg, int smblib_init(struct smb_charger *chg); int smblib_deinit(struct smb_charger *chg); #endif /* __SMB2_CHARGER_H */ - diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h index 8a49a8fb38ba..ba501761c209 100644 --- a/drivers/power/qcom-charger/smb-reg.h +++ b/drivers/power/qcom-charger/smb-reg.h @@ -52,6 +52,7 @@ enum { #define CHARGER_ERROR_STATUS_BAT_OV_BIT BIT(5) #define CHARGER_ERROR_STATUS_BAT_TERM_MISSING_BIT BIT(4) #define BAT_TEMP_STATUS_MASK GENMASK(3, 0) +#define BAT_TEMP_STATUS_SOFT_LIMIT_MASK GENMASK(3, 2) #define BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT BIT(3) #define BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT BIT(2) #define BAT_TEMP_STATUS_TOO_HOT_BIT BIT(1) @@ -506,6 +507,15 @@ enum { #define CC_ORIENTATION_BIT BIT(1) #define CC_ATTACHED_BIT BIT(0) +#define TYPE_C_STATUS_5_REG (USBIN_BASE + 0x0F) +#define TRY_SOURCE_FAILED_BIT BIT(6) +#define TRY_SINK_FAILED_BIT BIT(5) +#define TIMER_STAGE_2_BIT BIT(4) +#define TYPEC_LEGACY_CABLE_STATUS_BIT BIT(3) +#define TYPEC_NONCOMP_LEGACY_CABLE_STATUS_BIT BIT(2) +#define TYPEC_TRYSOURCE_DETECT_STATUS_BIT BIT(1) +#define TYPEC_TRYSINK_DETECT_STATUS_BIT BIT(0) + /* USBIN Interrupt Bits */ #define TYPE_C_CHANGE_RT_STS_BIT BIT(7) #define USBIN_ICL_CHANGE_RT_STS_BIT BIT(6) @@ -555,6 +565,16 @@ enum { #define TYPE_C_UFP_MODE_BIT BIT(1) #define EN_80UA_180UA_CUR_SOURCE_BIT BIT(0) +#define TYPE_C_CFG_3_REG (USBIN_BASE + 0x5A) +#define TVBUS_DEBOUNCE_BIT BIT(7) +#define TYPEC_LEGACY_CABLE_INT_EN_BIT BIT(6) +#define TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_BIT BIT(5) +#define TYPEC_TRYSOURCE_DETECT_INT_EN_BIT BIT(4) +#define TYPEC_TRYSINK_DETECT_INT_EN_BIT BIT(3) +#define EN_TRYSINK_MODE_BIT BIT(2) +#define EN_LEGACY_CABLE_DETECTION_BIT BIT(1) +#define ALLOW_PD_DRING_UFP_TCCDB_BIT BIT(0) + #define USBIN_ADAPTER_ALLOW_CFG_REG (USBIN_BASE + 0x60) #define USBIN_ADAPTER_ALLOW_MASK GENMASK(3, 0) enum { @@ -585,6 +605,7 @@ enum { #define DCD_TIMEOUT_SEL_BIT BIT(5) #define OCD_CURRENT_SEL_BIT BIT(4) #define SLOW_PLUGIN_TIMER_EN_CFG_BIT BIT(3) +#define FLOAT_OPTIONS_MASK GENMASK(2, 0) #define FLOAT_DIS_CHGING_CFG_BIT BIT(2) #define SUSPEND_FLOAT_CFG_BIT BIT(1) #define FORCE_FLOAT_SDP_CFG_BIT BIT(0) @@ -612,7 +633,7 @@ enum { #define TYPEC_VBUS_ASSERT_INT_EN_BIT BIT(0) #define TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG (USBIN_BASE + 0x68) -#define EXIT_SNK_BASED_ON_CC BIT(7) +#define EXIT_SNK_BASED_ON_CC_BIT BIT(7) #define VCONN_EN_ORIENTATION_BIT BIT(6) #define TYPEC_VCONN_OVERCURR_INT_EN_BIT BIT(5) #define VCONN_EN_SRC_BIT BIT(4) @@ -975,4 +996,6 @@ enum { #define SYSOK_POL_BIT BIT(3) #define SYSOK_OPTIONS_MASK GENMASK(2, 0) +#define CFG_BUCKBOOST_FREQ_SELECT_BUCK_REG (MISC_BASE + 0xA0) + #endif /* __SMB2_CHARGER_REG_H */ diff --git a/drivers/power/qcom-charger/smb138x-charger.c b/drivers/power/qcom-charger/smb138x-charger.c index e8ec2f49f7eb..3db295b3e6e8 100644 --- a/drivers/power/qcom-charger/smb138x-charger.c +++ b/drivers/power/qcom-charger/smb138x-charger.c @@ -72,6 +72,13 @@ static struct smb_params v1_params = { .max_u = 6000000, .step_u = 25000, }, + .freq_buck = { + .name = "buck switching frequency", + .reg = CFG_BUCKBOOST_FREQ_SELECT_BUCK_REG, + .min_u = 500, + .max_u = 2000, + .step_u = 100, + }, }; struct smb_dt_props { @@ -471,6 +478,10 @@ static int smb138x_parallel_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_MAX: rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval); break; + case POWER_SUPPLY_PROP_BUCK_FREQ: + rc = smblib_set_charge_param(chg, &chg->param.freq_buck, + val->intval); + break; default: pr_err("parallel power supply set prop %d not supported\n", prop); @@ -1172,6 +1183,7 @@ static int smb138x_probe(struct platform_device *pdev) chip->chg.dev = &pdev->dev; chip->chg.debug_mask = &__debug_mask; + chip->chg.name = "SMB"; chip->chg.regmap = dev_get_regmap(chip->chg.dev->parent, NULL); if (!chip->chg.regmap) { diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 2bc74941abc8..8c43effadc70 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -821,4 +821,12 @@ config QCOM_SMCINVOKE Enable SMCInvoke driver which supports capability based secure communication between QSEE and HLOS. +config QCOM_EARLY_RANDOM + bool "Initialize random pool very early" + help + The standard random pool may not initialize until late in the boot + process which means that any calls to get random numbers before then + may not be truly random. Select this option to make an early call + to get some random data to put in the pool. If unsure, say N. + source "drivers/soc/qcom/memshare/Kconfig" diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 434a114c000f..0105e03b082d 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -99,3 +99,4 @@ obj-$(CONFIG_MSM_RPM_LOG) += rpm_log.o obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o +obj-$(CONFIG_QCOM_EARLY_RANDOM) += early_random.o diff --git a/drivers/soc/qcom/early_random.c b/drivers/soc/qcom/early_random.c new file mode 100644 index 000000000000..d1ab39b16c81 --- /dev/null +++ b/drivers/soc/qcom/early_random.c @@ -0,0 +1,63 @@ +/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/random.h> + +#include <soc/qcom/scm.h> + +#include <asm/io.h> +#include <asm/cacheflush.h> + +#define TZ_SVC_CRYPTO 10 +#define PRNG_CMD_ID 0x01 + +struct tz_prng_data { + uint8_t *out_buf; + uint32_t out_buf_sz; +} __packed; + +DEFINE_SCM_BUFFER(common_scm_buf) +#define RANDOM_BUFFER_SIZE PAGE_SIZE +char random_buffer[RANDOM_BUFFER_SIZE] __aligned(PAGE_SIZE); + +void __init init_random_pool(void) +{ + struct tz_prng_data data; + int ret; + u32 resp; + struct scm_desc desc; + + data.out_buf = (uint8_t *) virt_to_phys(random_buffer); + desc.args[0] = (unsigned long) data.out_buf; + desc.args[1] = data.out_buf_sz = SZ_512; + desc.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL); + + dmac_flush_range(random_buffer, random_buffer + RANDOM_BUFFER_SIZE); + + if (!is_scm_armv8()) + ret = scm_call_noalloc(TZ_SVC_CRYPTO, PRNG_CMD_ID, &data, + sizeof(data), &resp, sizeof(resp), + common_scm_buf, + SCM_BUFFER_SIZE(common_scm_buf)); + else + ret = scm_call2(SCM_SIP_FNID(TZ_SVC_CRYPTO, PRNG_CMD_ID), + &desc); + + if (!ret) { + dmac_inv_range(random_buffer, random_buffer + + RANDOM_BUFFER_SIZE); + add_device_randomness(random_buffer, SZ_512); + } +} + diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index f47d4a51fccd..2d3d96fe80e1 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -107,6 +107,18 @@ module_param(qmi_timeout, ulong, 0600); #define WCSS_CLK_CTL_WCSS_CSS_GDSCR_HW_CONTROL BIT(1) #define WCSS_CLK_CTL_WCSS_CSS_GDSCR_PWR_ON BIT(31) +#define WCSS_CLK_CTL_NOC_CMD_RCGR_OFFSET 0x1D1030 +#define WCSS_CLK_CTL_NOC_CMD_RCGR_UPDATE BIT(0) + +#define WCSS_CLK_CTL_NOC_CFG_RCGR_OFFSET 0x1D1034 +#define WCSS_CLK_CTL_NOC_CFG_RCGR_SRC_SEL GENMASK(10, 8) + +#define WCSS_CLK_CTL_REF_CMD_RCGR_OFFSET 0x1D602C +#define WCSS_CLK_CTL_REF_CMD_RCGR_UPDATE BIT(0) + +#define WCSS_CLK_CTL_REF_CFG_RCGR_OFFSET 0x1D6030 +#define WCSS_CLK_CTL_REF_CFG_RCGR_SRC_SEL GENMASK(10, 8) + /* * Registers: WCSS_HM_A_WIFI_APB_3_A_WCMN_MAC_WCMN_REG * Base Address: 0x18AF0000 @@ -1084,7 +1096,7 @@ static void icnss_hw_io_reset(struct icnss_priv *priv, bool on) } } -int icnss_hw_reset_wlan_ss_power_down(struct icnss_priv *priv) +static int icnss_hw_reset_wlan_ss_power_down(struct icnss_priv *priv) { u32 rdata; @@ -1116,7 +1128,7 @@ int icnss_hw_reset_wlan_ss_power_down(struct icnss_priv *priv) return 0; } -int icnss_hw_reset_common_ss_power_down(struct icnss_priv *priv) +static int icnss_hw_reset_common_ss_power_down(struct icnss_priv *priv) { u32 rdata; @@ -1161,7 +1173,7 @@ int icnss_hw_reset_common_ss_power_down(struct icnss_priv *priv) } -int icnss_hw_reset_wlan_rfactrl_power_down(struct icnss_priv *priv) +static int icnss_hw_reset_wlan_rfactrl_power_down(struct icnss_priv *priv) { u32 rdata; @@ -1191,7 +1203,7 @@ int icnss_hw_reset_wlan_rfactrl_power_down(struct icnss_priv *priv) return 0; } -void icnss_hw_wsi_cmd_error_recovery(struct icnss_priv *priv) +static void icnss_hw_wsi_cmd_error_recovery(struct icnss_priv *priv) { icnss_pr_dbg("RESET: WSI CMD Error recovery, state: 0x%lx\n", priv->state); @@ -1215,7 +1227,7 @@ void icnss_hw_wsi_cmd_error_recovery(struct icnss_priv *priv) PMM_WSI_CMD_SW_BUS_SYNC, 0); } -u32 icnss_hw_rf_register_read_command(struct icnss_priv *priv, u32 addr) +static u32 icnss_hw_rf_register_read_command(struct icnss_priv *priv, u32 addr) { u32 rdata = 0; int ret; @@ -1264,7 +1276,7 @@ u32 icnss_hw_rf_register_read_command(struct icnss_priv *priv, u32 addr) return rdata; } -int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv) +static int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv) { u32 rdata; int ret; @@ -1318,7 +1330,30 @@ int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv) return 0; } -int icnss_hw_reset_xo_disable_cmd(struct icnss_priv *priv) +static int icnss_hw_reset_switch_to_cxo(struct icnss_priv *priv) +{ + icnss_pr_dbg("RESET: Switch to CXO, state: 0x%lx\n", priv->state); + + icnss_hw_write_reg_field(priv->mem_base_va, + WCSS_CLK_CTL_NOC_CFG_RCGR_OFFSET, + WCSS_CLK_CTL_NOC_CFG_RCGR_SRC_SEL, 0); + + icnss_hw_write_reg_field(priv->mem_base_va, + WCSS_CLK_CTL_NOC_CMD_RCGR_OFFSET, + WCSS_CLK_CTL_NOC_CMD_RCGR_UPDATE, 1); + + icnss_hw_write_reg_field(priv->mem_base_va, + WCSS_CLK_CTL_REF_CFG_RCGR_OFFSET, + WCSS_CLK_CTL_REF_CFG_RCGR_SRC_SEL, 0); + + icnss_hw_write_reg_field(priv->mem_base_va, + WCSS_CLK_CTL_REF_CMD_RCGR_OFFSET, + WCSS_CLK_CTL_REF_CMD_RCGR_UPDATE, 1); + + return 0; +} + +static int icnss_hw_reset_xo_disable_cmd(struct icnss_priv *priv) { int ret; @@ -1366,7 +1401,7 @@ int icnss_hw_reset_xo_disable_cmd(struct icnss_priv *priv) return 0; } -int icnss_hw_reset(struct icnss_priv *priv) +static int icnss_hw_reset(struct icnss_priv *priv) { u32 rdata; u32 rdata1; @@ -1424,6 +1459,8 @@ int icnss_hw_reset(struct icnss_priv *priv) icnss_hw_reset_rf_reset_cmd(priv); + icnss_hw_reset_switch_to_cxo(priv); + icnss_hw_reset_xo_disable_cmd(priv); icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET, @@ -2947,7 +2984,7 @@ int icnss_register_driver(struct icnss_driver_ops *ops) ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER, ICNSS_EVENT_SYNC, ops); - if (ret == -ERESTARTSYS) + if (ret == -EINTR) ret = 0; out: diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index 504a3263253c..8cba88742cb8 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -462,7 +462,7 @@ static int ssr_event_notify(struct notifier_block *this, struct qmi_client_info, ssr_notifier); struct notif_data *notif = data; switch (code) { - case SUBSYS_AFTER_SHUTDOWN: + case SUBSYS_BEFORE_SHUTDOWN: pr_debug("Root PD DOWN(SSR notification), crashed?%d\n", notif->crashed); if (notif->crashed) @@ -605,8 +605,8 @@ void *service_notif_register_notifier(const char *service_path, int instance_id, if (!service_path || !instance_id || !nb) return ERR_PTR(-EINVAL); - service_notif = _find_service_info(service_path); mutex_lock(¬if_add_lock); + service_notif = _find_service_info(service_path); if (!service_notif) { service_notif = (struct service_notif_info *)add_service_notif( service_path, diff --git a/drivers/thermal/lmh_lite.c b/drivers/thermal/lmh_lite.c index 32a573d22270..44ceb723d34c 100644 --- a/drivers/thermal/lmh_lite.c +++ b/drivers/thermal/lmh_lite.c @@ -70,8 +70,8 @@ int idx = 0; \ desc_arg.args[cmd_idx] = cmd_buf.list_start = next; \ trace_lmh_event_call("GET_TYPE enter"); \ - dmac_flush_range(payload, payload + sizeof(uint32_t) * \ - LMH_SCM_PAYLOAD_SIZE); \ + dmac_flush_range(payload, (void *)payload + \ + sizeof(uint32_t) * LMH_SCM_PAYLOAD_SIZE);\ if (!is_scm_armv8()) { \ ret = scm_call(SCM_SVC_LMH, cmd_id, \ (void *) &cmd_buf, SCM_BUFFER_SIZE(cmd_buf), \ @@ -321,7 +321,8 @@ static void lmh_read_and_update(struct lmh_driver_data *lmh_dat) = SCM_BUFFER_SIZE(struct lmh_sensor_packet); desc_arg.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL); trace_lmh_event_call("GET_INTENSITY enter"); - dmac_flush_range(&payload, &payload + sizeof(struct lmh_sensor_packet)); + dmac_flush_range(&payload, (void *)&payload + + sizeof(struct lmh_sensor_packet)); if (!is_scm_armv8()) ret = scm_call(SCM_SVC_LMH, LMH_GET_INTENSITY, (void *) &cmd_buf, SCM_BUFFER_SIZE(cmd_buf), NULL, 0); @@ -664,7 +665,7 @@ static int lmh_get_sensor_list(void) lmh_sensor_packet); desc_arg.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL); trace_lmh_event_call("GET_SENSORS enter"); - dmac_flush_range(payload, payload + buf_size); + dmac_flush_range(payload, (void *)payload + buf_size); if (!is_scm_armv8()) ret = scm_call(SCM_SVC_LMH, LMH_GET_SENSORS, (void *) &cmd_buf, @@ -898,7 +899,7 @@ static int lmh_debug_read(struct lmh_debug_ops *ops, uint32_t **buf) desc_arg.args[1] = cmd_buf.buf_size = curr_size; desc_arg.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL); trace_lmh_event_call("GET_DEBUG_READ enter"); - dmac_flush_range(payload, payload + curr_size); + dmac_flush_range(payload, (void *)payload + curr_size); if (!is_scm_armv8()) { ret = scm_call(SCM_SVC_LMH, LMH_DEBUG_READ, (void *) &cmd_buf, SCM_BUFFER_SIZE(cmd_buf), @@ -968,7 +969,7 @@ static int lmh_debug_config_write(uint32_t cmd_id, uint32_t *buf, int size) desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL, SCM_VAL, SCM_VAL); trace_lmh_event_call("CONFIG_DEBUG_WRITE enter"); - dmac_flush_range(payload, payload + size_bytes); + dmac_flush_range(payload, (void *)payload + size_bytes); if (!is_scm_armv8()) ret = scm_call(SCM_SVC_LMH, cmd_id, (void *) &cmd_buf, SCM_BUFFER_SIZE(cmd_buf), NULL, 0); diff --git a/drivers/thermal/msm_lmh_dcvs.c b/drivers/thermal/msm_lmh_dcvs.c index cd45eeccbfe7..fbe76eaa3867 100644 --- a/drivers/thermal/msm_lmh_dcvs.c +++ b/drivers/thermal/msm_lmh_dcvs.c @@ -164,7 +164,7 @@ static int msm_lmh_dcvs_write(uint32_t node_id, uint32_t fn, desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL, SCM_VAL, SCM_VAL); - dmac_flush_range(payload, payload + 5 * (sizeof(uint32_t))); + dmac_flush_range(payload, (void *)payload + 5 * (sizeof(uint32_t))); ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, MSM_LIMITS_DCVSH), &desc_arg); kfree(payload); diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c index 17ecd61e9ee6..ced2f23addd4 100644 --- a/drivers/thermal/msm_thermal.c +++ b/drivers/thermal/msm_thermal.c @@ -1019,7 +1019,7 @@ static int msm_lmh_dcvs_write(uint32_t node_id, uint32_t fn, uint32_t setting, desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL, SCM_VAL, SCM_VAL); - dmac_flush_range(payload, payload + 5 * (sizeof(uint32_t))); + dmac_flush_range(payload, (void *)payload + 5 * (sizeof(uint32_t))); ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, MSM_LIMITS_DCVSH), &desc_arg); kfree(payload); diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index fa3c9e511663..ec9288791667 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -3555,6 +3555,7 @@ static int msm_hs_probe(struct platform_device *pdev) } msm_serial_debugfs_init(msm_uport, pdev->id); + msm_hs_unconfig_uart_gpios(uport); uport->line = pdev->id; if (pdata->userid && pdata->userid <= UARTDM_NR) diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 0121bf7ca4ac..98d5908c1e2f 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1139,6 +1139,7 @@ static void f_midi_free(struct usb_function *f) mutex_lock(&opts->lock); for (i = opts->in_ports - 1; i >= 0; --i) kfree(midi->in_port[i]); + opts->func_inst.f = NULL; kfree(midi); --opts->refcnt; mutex_unlock(&opts->lock); diff --git a/drivers/video/fbdev/msm/mdss_mdp_layer.c b/drivers/video/fbdev/msm/mdss_mdp_layer.c index 0f0df2256f74..e26b3843d7b0 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_layer.c +++ b/drivers/video/fbdev/msm/mdss_mdp_layer.c @@ -1618,8 +1618,8 @@ static bool __multirect_validate_rects(struct mdp_input_layer **layers, /* resolution related validation */ if (mdss_rect_overlap_check(&dst[0], &dst[1])) { pr_err("multirect dst overlap is not allowed. input: %d,%d,%d,%d paired %d,%d,%d,%d\n", - dst[0].x, dst[0].y, dst[0].w, dst[0].y, - dst[1].x, dst[1].y, dst[1].w, dst[1].y); + dst[0].x, dst[0].y, dst[0].w, dst[0].h, + dst[1].x, dst[1].y, dst[1].w, dst[1].h); return false; } diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 218cd875ee5a..a8ccecf5799a 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -198,6 +198,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_FLASH_CURRENT_MAX, POWER_SUPPLY_PROP_UPDATE_NOW, POWER_SUPPLY_PROP_ESR_COUNT, + POWER_SUPPLY_PROP_BUCK_FREQ, POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE, POWER_SUPPLY_PROP_CHARGE_DONE, POWER_SUPPLY_PROP_FLASH_ACTIVE, @@ -222,6 +223,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, POWER_SUPPLY_PROP_PARALLEL_DISABLE, + POWER_SUPPLY_PROP_PARALLEL_PERCENT, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ diff --git a/include/linux/sched.h b/include/linux/sched.h index a395d8a9ff73..06acefeffd4c 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -356,7 +356,7 @@ extern int lockdep_tasklist_lock_is_held(void); extern void sched_init(void); extern void sched_init_smp(void); extern asmlinkage void schedule_tail(struct task_struct *prev); -extern void init_idle(struct task_struct *idle, int cpu); +extern void init_idle(struct task_struct *idle, int cpu, bool hotplug); extern void init_idle_bootup_task(struct task_struct *idle); extern cpumask_var_t cpu_isolated_map; @@ -1332,11 +1332,15 @@ struct ravg { * sysctl_sched_ravg_hist_size windows. 'demand' could drive frequency * demand for tasks. * - * 'curr_window' represents task's contribution to cpu busy time - * statistics (rq->curr_runnable_sum) in current window + * 'curr_window_cpu' represents task's contribution to cpu busy time on + * various CPUs in the current window * - * 'prev_window' represents task's contribution to cpu busy time - * statistics (rq->prev_runnable_sum) in previous window + * 'prev_window_cpu' represents task's contribution to cpu busy time on + * various CPUs in the previous window + * + * 'curr_window' represents the sum of all entries in curr_window_cpu + * + * 'prev_window' represents the sum of all entries in prev_window_cpu * * 'pred_demand' represents task's current predicted cpu busy time * @@ -1346,6 +1350,7 @@ struct ravg { u64 mark_start; u32 sum, demand; u32 sum_history[RAVG_HIST_SIZE_MAX]; + u32 *curr_window_cpu, *prev_window_cpu; u32 curr_window, prev_window; u16 active_windows; u32 pred_demand; diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h index 9fe71c774543..6848454c5447 100644 --- a/include/linux/sched/sysctl.h +++ b/include/linux/sched/sysctl.h @@ -44,6 +44,7 @@ extern unsigned int sysctl_sched_wake_to_idle; #ifdef CONFIG_SCHED_HMP extern int sysctl_sched_freq_inc_notify; extern int sysctl_sched_freq_dec_notify; +extern unsigned int sysctl_sched_freq_reporting_policy; extern unsigned int sysctl_sched_window_stats_policy; extern unsigned int sysctl_sched_ravg_hist_size; extern unsigned int sysctl_sched_cpu_high_irqload; diff --git a/include/linux/types.h b/include/linux/types.h index 70dd3dfde631..9f2d2f46b459 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -9,6 +9,9 @@ #define DECLARE_BITMAP(name,bits) \ unsigned long name[BITS_TO_LONGS(bits)] +#define DECLARE_BITMAP_ARRAY(name,nr,bits) \ + unsigned long name[nr][BITS_TO_LONGS(bits)] + typedef __u32 __kernel_dev_t; typedef __kernel_fd_set fd_set; diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index daf69b7df534..cd15ae7b8b0c 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -260,6 +260,30 @@ TRACE_EVENT(sched_set_boost, TP_printk("ref_count=%d", __entry->ref_count) ); +#if defined(CREATE_TRACE_POINTS) && defined(CONFIG_SCHED_HMP) +static inline void __window_data(u32 *dst, u32 *src) +{ + if (src) + memcpy(dst, src, nr_cpu_ids * sizeof(u32)); + else + memset(dst, 0, nr_cpu_ids * sizeof(u32)); +} + +struct trace_seq; +const char *__window_print(struct trace_seq *p, const u32 *buf, int buf_len) +{ + int i; + const char *ret = p->buffer + seq_buf_used(&p->seq); + + for (i = 0; i < buf_len; i++) + trace_seq_printf(p, "%u ", buf[i]); + + trace_seq_putc(p, 0); + + return ret; +} +#endif + TRACE_EVENT(sched_update_task_ravg, TP_PROTO(struct task_struct *p, struct rq *rq, enum task_event evt, @@ -288,13 +312,17 @@ TRACE_EVENT(sched_update_task_ravg, __field( u64, rq_ps ) __field( u64, grp_cs ) __field( u64, grp_ps ) - __field( u64, grp_nt_cs ) - __field( u64, grp_nt_ps ) + __field( u64, grp_nt_cs ) + __field( u64, grp_nt_ps ) __field( u32, curr_window ) __field( u32, prev_window ) + __dynamic_array(u32, curr_sum, nr_cpu_ids ) + __dynamic_array(u32, prev_sum, nr_cpu_ids ) __field( u64, nt_cs ) __field( u64, nt_ps ) __field( u32, active_windows ) + __field( u8, curr_top ) + __field( u8, prev_top ) ), TP_fast_assign( @@ -321,22 +349,30 @@ TRACE_EVENT(sched_update_task_ravg, __entry->grp_nt_ps = cpu_time ? cpu_time->nt_prev_runnable_sum : 0; __entry->curr_window = p->ravg.curr_window; __entry->prev_window = p->ravg.prev_window; + __window_data(__get_dynamic_array(curr_sum), p->ravg.curr_window_cpu); + __window_data(__get_dynamic_array(prev_sum), p->ravg.prev_window_cpu); __entry->nt_cs = rq->nt_curr_runnable_sum; __entry->nt_ps = rq->nt_prev_runnable_sum; __entry->active_windows = p->ravg.active_windows; + __entry->curr_top = rq->curr_top; + __entry->prev_top = rq->prev_top; ), - TP_printk("wc %llu ws %llu delta %llu event %s cpu %d cur_freq %u cur_pid %d task %d (%s) ms %llu delta %llu demand %u sum %u irqtime %llu pred_demand %u rq_cs %llu rq_ps %llu cur_window %u prev_window %u nt_cs %llu nt_ps %llu active_wins %u grp_cs %lld grp_ps %lld, grp_nt_cs %llu, grp_nt_ps: %llu" - , __entry->wallclock, __entry->win_start, __entry->delta, + TP_printk("wc %llu ws %llu delta %llu event %s cpu %d cur_freq %u cur_pid %d task %d (%s) ms %llu delta %llu demand %u sum %u irqtime %llu pred_demand %u rq_cs %llu rq_ps %llu cur_window %u (%s) prev_window %u (%s) nt_cs %llu nt_ps %llu active_wins %u grp_cs %lld grp_ps %lld, grp_nt_cs %llu, grp_nt_ps: %llu curr_top %u prev_top %u", + __entry->wallclock, __entry->win_start, __entry->delta, task_event_names[__entry->evt], __entry->cpu, __entry->cur_freq, __entry->cur_pid, __entry->pid, __entry->comm, __entry->mark_start, __entry->delta_m, __entry->demand, __entry->sum, __entry->irqtime, __entry->pred_demand, __entry->rq_cs, __entry->rq_ps, __entry->curr_window, - __entry->prev_window, __entry->nt_cs, __entry->nt_ps, + __window_print(p, __get_dynamic_array(curr_sum), nr_cpu_ids), + __entry->prev_window, + __window_print(p, __get_dynamic_array(prev_sum), nr_cpu_ids), + __entry->nt_cs, __entry->nt_ps, __entry->active_windows, __entry->grp_cs, - __entry->grp_ps, __entry->grp_nt_cs, __entry->grp_nt_ps) + __entry->grp_ps, __entry->grp_nt_cs, __entry->grp_nt_ps, + __entry->curr_top, __entry->prev_top) ); TRACE_EVENT(sched_get_task_cpu_cycles, diff --git a/init/main.c b/init/main.c index fbafa271531c..7d4532bff5da 100644 --- a/init/main.c +++ b/init/main.c @@ -505,11 +505,6 @@ asmlinkage __visible void __init start_kernel(void) smp_setup_processor_id(); debug_objects_early_init(); - /* - * Set up the the initial canary ASAP: - */ - boot_init_stack_canary(); - cgroup_init_early(); local_irq_disable(); @@ -523,6 +518,10 @@ asmlinkage __visible void __init start_kernel(void) page_address_init(); pr_notice("%s", linux_banner); setup_arch(&command_line); + /* + * Set up the the initial canary ASAP: + */ + boot_init_stack_canary(); mm_init_cpumask(&init_mm); setup_command_line(command_line); setup_nr_cpu_ids(); diff --git a/kernel/fork.c b/kernel/fork.c index e89d0bae6f20..8a5962276788 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1684,7 +1684,7 @@ struct task_struct *fork_idle(int cpu) task = copy_process(CLONE_VM, 0, 0, NULL, &init_struct_pid, 0, 0); if (!IS_ERR(task)) { init_idle_pids(task->pids); - init_idle(task, cpu); + init_idle(task, cpu, false); } return task; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 024fb1007c78..7e7e19ed53c6 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -2255,13 +2255,13 @@ void __dl_clear_params(struct task_struct *p) void sched_exit(struct task_struct *p) { unsigned long flags; - int cpu = get_cpu(); - struct rq *rq = cpu_rq(cpu); + struct rq *rq; u64 wallclock; sched_set_group_id(p, 0); - raw_spin_lock_irqsave(&rq->lock, flags); + rq = task_rq_lock(p, &flags); + /* rq->curr == p */ wallclock = sched_ktime_clock(); update_task_ravg(rq->curr, rq, TASK_UPDATE, wallclock, 0); @@ -2269,11 +2269,13 @@ void sched_exit(struct task_struct *p) reset_task_stats(p); p->ravg.mark_start = wallclock; p->ravg.sum_history[0] = EXITING_TASK_MARKER; + + kfree(p->ravg.curr_window_cpu); + kfree(p->ravg.prev_window_cpu); + enqueue_task(rq, p, 0); clear_ed_task(p, rq); - raw_spin_unlock_irqrestore(&rq->lock, flags); - - put_cpu(); + task_rq_unlock(rq, p, &flags); } #endif /* CONFIG_SCHED_HMP */ @@ -2377,6 +2379,7 @@ int sched_fork(unsigned long clone_flags, struct task_struct *p) int cpu = get_cpu(); __sched_fork(clone_flags, p); + init_new_task_load(p, false); /* * We mark the process as running here. This guarantees that * nobody will actually run it, and a signal or other external @@ -2562,7 +2565,6 @@ void wake_up_new_task(struct task_struct *p) struct rq *rq; raw_spin_lock_irqsave(&p->pi_lock, flags); - init_new_task_load(p); add_new_task_to_grp(p); /* Initialize new task's runnable average */ init_entity_runnable_average(&p->se); @@ -5210,17 +5212,21 @@ void init_idle_bootup_task(struct task_struct *idle) * init_idle - set up an idle thread for a given CPU * @idle: task in question * @cpu: cpu the idle task belongs to + * @cpu_up: differentiate between initial boot vs hotplug * * NOTE: this function does not set the idle thread's NEED_RESCHED * flag, to make booting more robust. */ -void init_idle(struct task_struct *idle, int cpu) +void init_idle(struct task_struct *idle, int cpu, bool cpu_up) { struct rq *rq = cpu_rq(cpu); unsigned long flags; __sched_fork(0, idle); + if (!cpu_up) + init_new_task_load(idle, true); + raw_spin_lock_irqsave(&idle->pi_lock, flags); raw_spin_lock(&rq->lock); @@ -8009,6 +8015,22 @@ void __init sched_init(void) rq->old_estimated_time = 0; rq->old_busy_time_group = 0; rq->hmp_stats.pred_demands_sum = 0; + rq->curr_table = 0; + rq->prev_top = 0; + rq->curr_top = 0; + + for (j = 0; j < NUM_TRACKED_WINDOWS; j++) { + memset(&rq->load_subs[j], 0, + sizeof(struct load_subtractions)); + + rq->top_tasks[j] = kcalloc(NUM_LOAD_INDICES, + sizeof(u8), GFP_NOWAIT); + + /* No other choice */ + BUG_ON(!rq->top_tasks[j]); + + clear_top_tasks_bitmap(rq->top_tasks_bitmap[j]); + } #endif rq->max_idle_balance_cost = sysctl_sched_migration_cost; @@ -8051,7 +8073,7 @@ void __init sched_init(void) * but because we are the idle thread, we just pick up running again * when this runqueue becomes "idle". */ - init_idle(current, smp_processor_id()); + init_idle(current, smp_processor_id(), false); calc_load_update = jiffies + LOAD_FREQ; diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index b6dc131f36a6..c8c4272c61d8 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -418,6 +418,7 @@ static void sched_debug_header(struct seq_file *m) P(min_capacity); P(max_capacity); P(sched_ravg_window); + P(sched_load_granule); #endif #undef PN #undef P diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 6362b864e2b1..98ae45174a40 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -3189,8 +3189,8 @@ retry: } } p->last_cpu_selected_ts = sched_ktime_clock(); - sbc_flag |= env.sbc_best_cluster_flag; out: + sbc_flag |= env.sbc_best_cluster_flag; rcu_read_unlock(); trace_sched_task_load(p, sched_boost(), env.reason, env.sync, env.need_idle, sbc_flag, target); diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c index 3d5de8ba70a2..b3dad1289ed4 100644 --- a/kernel/sched/hmp.c +++ b/kernel/sched/hmp.c @@ -590,6 +590,7 @@ static struct sched_cluster *alloc_new_cluster(const struct cpumask *cpus) cluster->dstate_wakeup_latency = 0; cluster->freq_init_done = false; + raw_spin_lock_init(&cluster->load_lock); cluster->cpus = *cpus; cluster->efficiency = arch_get_cpu_efficiency(cpumask_first(cpus)); @@ -647,6 +648,7 @@ void init_clusters(void) { bitmap_clear(all_cluster_ids, 0, NR_CPUS); init_cluster.cpus = *cpu_possible_mask; + raw_spin_lock_init(&init_cluster.load_lock); INIT_LIST_HEAD(&cluster_head); } @@ -788,6 +790,12 @@ __read_mostly unsigned int sysctl_sched_new_task_windows = 5; #define SCHED_FREQ_ACCOUNT_WAIT_TIME 0 /* + * This governs what load needs to be used when reporting CPU busy time + * to the cpufreq governor. + */ +__read_mostly unsigned int sysctl_sched_freq_reporting_policy; + +/* * For increase, send notification if * freq_required - cur_freq > sysctl_sched_freq_inc_notify */ @@ -823,15 +831,15 @@ unsigned int max_possible_capacity = 1024; /* max(rq->max_possible_capacity) */ unsigned int min_max_possible_capacity = 1024; /* min(rq->max_possible_capacity) */ -/* Window size (in ns) */ -__read_mostly unsigned int sched_ravg_window = 10000000; - /* Min window size (in ns) = 10ms */ #define MIN_SCHED_RAVG_WINDOW 10000000 /* Max window size (in ns) = 1s */ #define MAX_SCHED_RAVG_WINDOW 1000000000 +/* Window size (in ns) */ +__read_mostly unsigned int sched_ravg_window = MIN_SCHED_RAVG_WINDOW; + /* Temporarily disable window-stats activity on all cpus */ unsigned int __read_mostly sched_disable_window_stats; @@ -851,6 +859,21 @@ static DEFINE_RWLOCK(related_thread_group_lock); list_for_each_entry(grp, &related_thread_groups, list) /* + * Task load is categorized into buckets for the purpose of top task tracking. + * The entire range of load from 0 to sched_ravg_window needs to be covered + * in NUM_LOAD_INDICES number of buckets. Therefore the size of each bucket + * is given by sched_ravg_window / NUM_LOAD_INDICES. Since the default value + * of sched_ravg_window is MIN_SCHED_RAVG_WINDOW, use that to compute + * sched_load_granule. + */ +__read_mostly unsigned int sched_load_granule = + MIN_SCHED_RAVG_WINDOW / NUM_LOAD_INDICES; + +/* Size of bitmaps maintained to track top tasks */ +static const unsigned int top_tasks_bitmap_size = + BITS_TO_LONGS(NUM_LOAD_INDICES + 1) * sizeof(unsigned long); + +/* * Demand aggregation for frequency purpose: * * 'sched_freq_aggregate' controls aggregation of cpu demand of related threads @@ -1505,7 +1528,7 @@ static inline int invalid_value(unsigned int *data) /* * Handle "atomic" update of sysctl_sched_window_stats_policy, - * sysctl_sched_ravg_hist_size and sched_freq_legacy_mode variables. + * sysctl_sched_ravg_hist_size variables. */ int sched_window_update_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, @@ -1611,7 +1634,7 @@ unsigned int cpu_temp(int cpu) return 0; } -void init_new_task_load(struct task_struct *p) +void init_new_task_load(struct task_struct *p, bool idle_task) { int i; u32 init_load_windows = sched_init_task_load_windows; @@ -1623,6 +1646,15 @@ void init_new_task_load(struct task_struct *p) memset(&p->ravg, 0, sizeof(struct ravg)); p->cpu_cycles = 0; + p->ravg.curr_window_cpu = kcalloc(nr_cpu_ids, sizeof(u32), GFP_ATOMIC); + p->ravg.prev_window_cpu = kcalloc(nr_cpu_ids, sizeof(u32), GFP_ATOMIC); + + /* Don't have much choice. CPU frequency would be bogus */ + BUG_ON(!p->ravg.curr_window_cpu || !p->ravg.prev_window_cpu); + + if (idle_task) + return; + if (init_load_pct) init_load_windows = div64_u64((u64)init_load_pct * (u64)sched_ravg_window, 100); @@ -2161,6 +2193,174 @@ void update_task_pred_demand(struct rq *rq, struct task_struct *p, int event) p->ravg.pred_demand = new; } +void clear_top_tasks_bitmap(unsigned long *bitmap) +{ + memset(bitmap, 0, top_tasks_bitmap_size); + __set_bit(NUM_LOAD_INDICES, bitmap); +} + +/* + * Special case the last index and provide a fast path for index = 0. + * Note that sched_load_granule can change underneath us if we are not + * holding any runqueue locks while calling the two functions below. + */ +static u32 top_task_load(struct rq *rq) +{ + int index = rq->prev_top; + u8 prev = 1 - rq->curr_table; + + if (!index) { + int msb = NUM_LOAD_INDICES - 1; + + if (!test_bit(msb, rq->top_tasks_bitmap[prev])) + return 0; + else + return sched_load_granule; + } else if (index == NUM_LOAD_INDICES - 1) { + return sched_ravg_window; + } else { + return (index + 1) * sched_load_granule; + } +} + +static int load_to_index(u32 load) +{ + if (load < sched_load_granule) + return 0; + else if (load >= sched_ravg_window) + return NUM_LOAD_INDICES - 1; + else + return load / sched_load_granule; +} + +static void update_top_tasks(struct task_struct *p, struct rq *rq, + u32 old_curr_window, int new_window, bool full_window) +{ + u8 curr = rq->curr_table; + u8 prev = 1 - curr; + u8 *curr_table = rq->top_tasks[curr]; + u8 *prev_table = rq->top_tasks[prev]; + int old_index, new_index, update_index; + u32 curr_window = p->ravg.curr_window; + u32 prev_window = p->ravg.prev_window; + bool zero_index_update; + + if (old_curr_window == curr_window && !new_window) + return; + + old_index = load_to_index(old_curr_window); + new_index = load_to_index(curr_window); + + if (!new_window) { + zero_index_update = !old_curr_window && curr_window; + if (old_index != new_index || zero_index_update) { + if (old_curr_window) + curr_table[old_index] -= 1; + if (curr_window) + curr_table[new_index] += 1; + if (new_index > rq->curr_top) + rq->curr_top = new_index; + } + + if (!curr_table[old_index]) + __clear_bit(NUM_LOAD_INDICES - old_index - 1, + rq->top_tasks_bitmap[curr]); + + if (curr_table[new_index] == 1) + __set_bit(NUM_LOAD_INDICES - new_index - 1, + rq->top_tasks_bitmap[curr]); + + return; + } + + /* + * The window has rolled over for this task. By the time we get + * here, curr/prev swaps would has already occurred. So we need + * to use prev_window for the new index. + */ + update_index = load_to_index(prev_window); + + if (full_window) { + /* + * Two cases here. Either 'p' ran for the entire window or + * it didn't run at all. In either case there is no entry + * in the prev table. If 'p' ran the entire window, we just + * need to create a new entry in the prev table. In this case + * update_index will be correspond to sched_ravg_window + * so we can unconditionally update the top index. + */ + if (prev_window) { + prev_table[update_index] += 1; + rq->prev_top = update_index; + } + + if (prev_table[update_index] == 1) + __set_bit(NUM_LOAD_INDICES - update_index - 1, + rq->top_tasks_bitmap[prev]); + } else { + zero_index_update = !old_curr_window && prev_window; + if (old_index != update_index || zero_index_update) { + if (old_curr_window) + prev_table[old_index] -= 1; + + prev_table[update_index] += 1; + + if (update_index > rq->prev_top) + rq->prev_top = update_index; + + if (!prev_table[old_index]) + __clear_bit(NUM_LOAD_INDICES - old_index - 1, + rq->top_tasks_bitmap[prev]); + + if (prev_table[update_index] == 1) + __set_bit(NUM_LOAD_INDICES - update_index - 1, + rq->top_tasks_bitmap[prev]); + } + } + + if (curr_window) { + curr_table[new_index] += 1; + + if (new_index > rq->curr_top) + rq->curr_top = new_index; + + if (curr_table[new_index] == 1) + __set_bit(NUM_LOAD_INDICES - new_index - 1, + rq->top_tasks_bitmap[curr]); + } +} + +static inline void clear_top_tasks_table(u8 *table) +{ + memset(table, 0, NUM_LOAD_INDICES * sizeof(u8)); +} + +static u32 empty_windows[NR_CPUS]; + +static void rollover_task_window(struct task_struct *p, bool full_window) +{ + u32 *curr_cpu_windows = empty_windows; + u32 curr_window; + int i; + + /* Rollover the sum */ + curr_window = 0; + + if (!full_window) { + curr_window = p->ravg.curr_window; + curr_cpu_windows = p->ravg.curr_window_cpu; + } + + p->ravg.prev_window = curr_window; + p->ravg.curr_window = 0; + + /* Roll over individual CPU contributions */ + for (i = 0; i < nr_cpu_ids; i++) { + p->ravg.prev_window_cpu[i] = curr_cpu_windows[i]; + p->ravg.curr_window_cpu[i] = 0; + } +} + /* * Account cpu activity in its busy time counters (rq->curr/prev_runnable_sum) */ @@ -2181,6 +2381,8 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, int prev_sum_reset = 0; bool new_task; struct related_thread_group *grp; + int cpu = rq->cpu; + u32 old_curr_window; new_window = mark_start < window_start; if (new_window) { @@ -2240,57 +2442,43 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, * Handle per-task window rollover. We don't care about the idle * task or exiting tasks. */ - if (new_window && !is_idle_task(p) && !exiting_task(p)) { - u32 curr_window = 0; + if (!is_idle_task(p) && !exiting_task(p)) { + old_curr_window = p->ravg.curr_window; - if (!full_window) - curr_window = p->ravg.curr_window; - - p->ravg.prev_window = curr_window; - p->ravg.curr_window = 0; + if (new_window) + rollover_task_window(p, full_window); } if (flip_counters) { u64 curr_sum = *curr_runnable_sum; u64 nt_curr_sum = *nt_curr_runnable_sum; + u8 curr_table = rq->curr_table; + u8 prev_table = 1 - curr_table; + int curr_top = rq->curr_top; - if (prev_sum_reset) + clear_top_tasks_table(rq->top_tasks[prev_table]); + clear_top_tasks_bitmap(rq->top_tasks_bitmap[prev_table]); + + if (prev_sum_reset) { curr_sum = nt_curr_sum = 0; + curr_top = 0; + clear_top_tasks_table(rq->top_tasks[curr_table]); + clear_top_tasks_bitmap( + rq->top_tasks_bitmap[curr_table]); + } *prev_runnable_sum = curr_sum; *nt_prev_runnable_sum = nt_curr_sum; *curr_runnable_sum = 0; *nt_curr_runnable_sum = 0; + rq->curr_table = prev_table; + rq->prev_top = curr_top; + rq->curr_top = 0; } - if (!account_busy_for_cpu_time(rq, p, irqtime, event)) { - /* - * account_busy_for_cpu_time() = 0, so no update to the - * task's current window needs to be made. This could be - * for example - * - * - a wakeup event on a task within the current - * window (!new_window below, no action required), - * - switching to a new task from idle (PICK_NEXT_TASK) - * in a new window where irqtime is 0 and we aren't - * waiting on IO - */ - - if (!new_window) - return; - - /* - * A new window has started. The RQ demand must be rolled - * over if p is the current task. - */ - if (p_is_curr_task) { - /* p is idle task */ - BUG_ON(p != rq->idle); - } - - return; - } + if (!account_busy_for_cpu_time(rq, p, irqtime, event)) + goto done; if (!new_window) { /* @@ -2310,10 +2498,12 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, if (new_task) *nt_curr_runnable_sum += delta; - if (!is_idle_task(p) && !exiting_task(p)) + if (!is_idle_task(p) && !exiting_task(p)) { p->ravg.curr_window += delta; + p->ravg.curr_window_cpu[cpu] += delta; + } - return; + goto done; } if (!p_is_curr_task) { @@ -2336,8 +2526,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, * contribution to previous completed window. */ delta = scale_exec_time(window_start - mark_start, rq); - if (!exiting_task(p)) + if (!exiting_task(p)) { p->ravg.prev_window += delta; + p->ravg.prev_window_cpu[cpu] += delta; + } } else { /* * Since at least one full window has elapsed, @@ -2345,8 +2537,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, * full window (window_size). */ delta = scale_exec_time(window_size, rq); - if (!exiting_task(p)) + if (!exiting_task(p)) { p->ravg.prev_window = delta; + p->ravg.prev_window_cpu[cpu] = delta; + } } *prev_runnable_sum += delta; @@ -2359,10 +2553,12 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, if (new_task) *nt_curr_runnable_sum += delta; - if (!exiting_task(p)) + if (!exiting_task(p)) { p->ravg.curr_window = delta; + p->ravg.curr_window_cpu[cpu] = delta; + } - return; + goto done; } if (!irqtime || !is_idle_task(p) || cpu_is_waiting_on_io(rq)) { @@ -2386,8 +2582,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, * contribution to previous completed window. */ delta = scale_exec_time(window_start - mark_start, rq); - if (!is_idle_task(p) && !exiting_task(p)) + if (!is_idle_task(p) && !exiting_task(p)) { p->ravg.prev_window += delta; + p->ravg.prev_window_cpu[cpu] += delta; + } } else { /* * Since at least one full window has elapsed, @@ -2395,8 +2593,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, * full window (window_size). */ delta = scale_exec_time(window_size, rq); - if (!is_idle_task(p) && !exiting_task(p)) + if (!is_idle_task(p) && !exiting_task(p)) { p->ravg.prev_window = delta; + p->ravg.prev_window_cpu[cpu] = delta; + } } /* @@ -2413,10 +2613,12 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, if (new_task) *nt_curr_runnable_sum += delta; - if (!is_idle_task(p) && !exiting_task(p)) + if (!is_idle_task(p) && !exiting_task(p)) { p->ravg.curr_window = delta; + p->ravg.curr_window_cpu[cpu] = delta; + } - return; + goto done; } if (irqtime) { @@ -2461,7 +2663,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq, return; } - BUG(); +done: + if (!is_idle_task(p) && !exiting_task(p)) + update_top_tasks(p, rq, old_curr_window, + new_window, full_window); } static inline u32 predict_and_update_buckets(struct rq *rq, @@ -2829,11 +3034,23 @@ void sched_account_irqstart(int cpu, struct task_struct *curr, u64 wallclock) void reset_task_stats(struct task_struct *p) { u32 sum = 0; + u32 *curr_window_ptr = NULL; + u32 *prev_window_ptr = NULL; - if (exiting_task(p)) + if (exiting_task(p)) { sum = EXITING_TASK_MARKER; + } else { + curr_window_ptr = p->ravg.curr_window_cpu; + prev_window_ptr = p->ravg.prev_window_cpu; + memset(curr_window_ptr, 0, sizeof(u32) * nr_cpu_ids); + memset(prev_window_ptr, 0, sizeof(u32) * nr_cpu_ids); + } memset(&p->ravg, 0, sizeof(struct ravg)); + + p->ravg.curr_window_cpu = curr_window_ptr; + p->ravg.prev_window_cpu = prev_window_ptr; + /* Retain EXITING_TASK marker */ p->ravg.sum_history[0] = sum; } @@ -2889,7 +3106,9 @@ static void reset_all_task_stats(void) read_lock(&tasklist_lock); do_each_thread(g, p) { + raw_spin_lock(&p->pi_lock); reset_task_stats(p); + raw_spin_unlock(&p->pi_lock); } while_each_thread(g, p); read_unlock(&tasklist_lock); } @@ -2934,7 +3153,7 @@ const char *sched_window_reset_reasons[] = { /* Called with IRQs enabled */ void reset_all_window_stats(u64 window_start, unsigned int window_size) { - int cpu; + int cpu, i; unsigned long flags; u64 start_ts = sched_ktime_clock(); int reason = WINDOW_CHANGE; @@ -2968,6 +3187,7 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size) if (window_size) { sched_ravg_window = window_size * TICK_NSEC; set_hmp_defaults(); + sched_load_granule = sched_ravg_window / NUM_LOAD_INDICES; } enable_window_stats(); @@ -2979,6 +3199,16 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size) rq->window_start = window_start; rq->curr_runnable_sum = rq->prev_runnable_sum = 0; rq->nt_curr_runnable_sum = rq->nt_prev_runnable_sum = 0; + for (i = 0; i < NUM_TRACKED_WINDOWS; i++) { + memset(&rq->load_subs[i], 0, + sizeof(struct load_subtractions)); + clear_top_tasks_table(rq->top_tasks[i]); + clear_top_tasks_bitmap(rq->top_tasks_bitmap[i]); + } + + rq->curr_table = 0; + rq->curr_top = 0; + rq->prev_top = 0; reset_cpu_hmp_stats(cpu, 1); } @@ -3011,6 +3241,59 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size) sched_ktime_clock() - start_ts, reason, old, new); } +/* + * In this function we match the accumulated subtractions with the current + * and previous windows we are operating with. Ignore any entries where + * the window start in the load_subtraction struct does not match either + * the curent or the previous window. This could happen whenever CPUs + * become idle or busy with interrupts disabled for an extended period. + */ +static inline void account_load_subtractions(struct rq *rq) +{ + u64 ws = rq->window_start; + u64 prev_ws = ws - sched_ravg_window; + struct load_subtractions *ls = rq->load_subs; + int i; + + for (i = 0; i < NUM_TRACKED_WINDOWS; i++) { + if (ls[i].window_start == ws) { + rq->curr_runnable_sum -= ls[i].subs; + rq->nt_curr_runnable_sum -= ls[i].new_subs; + } else if (ls[i].window_start == prev_ws) { + rq->prev_runnable_sum -= ls[i].subs; + rq->nt_prev_runnable_sum -= ls[i].new_subs; + } + + ls[i].subs = 0; + ls[i].new_subs = 0; + } + + BUG_ON((s64)rq->prev_runnable_sum < 0); + BUG_ON((s64)rq->curr_runnable_sum < 0); + BUG_ON((s64)rq->nt_prev_runnable_sum < 0); + BUG_ON((s64)rq->nt_curr_runnable_sum < 0); +} + +static inline u64 freq_policy_load(struct rq *rq, u64 load) +{ + unsigned int reporting_policy = sysctl_sched_freq_reporting_policy; + + switch (reporting_policy) { + case FREQ_REPORT_MAX_CPU_LOAD_TOP_TASK: + load = max_t(u64, load, top_task_load(rq)); + break; + case FREQ_REPORT_TOP_TASK: + load = top_task_load(rq); + break; + case FREQ_REPORT_CPU_LOAD: + break; + default: + WARN_ON_ONCE(1); + } + + return load; +} + static inline void sync_window_start(struct rq *rq, struct group_cpu_time *cpu_time); @@ -3033,6 +3316,7 @@ void sched_get_cpus_busy(struct sched_load *busy, struct related_thread_group *grp; u64 total_group_load = 0, total_ngload = 0; bool aggregate_load = false; + struct sched_cluster *cluster = cpu_cluster(cpumask_first(query_cpus)); if (unlikely(cpus == 0)) return; @@ -3050,6 +3334,13 @@ void sched_get_cpus_busy(struct sched_load *busy, window_size = sched_ravg_window; + /* + * We don't really need the cluster lock for this entire for loop + * block. However, there is no advantage in optimizing this as rq + * locks are held regardless and would prevent migration anyways + */ + raw_spin_lock(&cluster->load_lock); + for_each_cpu(cpu, query_cpus) { rq = cpu_rq(cpu); @@ -3057,6 +3348,7 @@ void sched_get_cpus_busy(struct sched_load *busy, 0); cur_freq[i] = cpu_cycles_to_freq(rq->cc.cycles, rq->cc.time); + account_load_subtractions(rq); load[i] = rq->old_busy_time = rq->prev_runnable_sum; nload[i] = rq->nt_prev_runnable_sum; pload[i] = rq->hmp_stats.pred_demands_sum; @@ -3083,6 +3375,8 @@ void sched_get_cpus_busy(struct sched_load *busy, i++; } + raw_spin_unlock(&cluster->load_lock); + for_each_related_thread_group(grp) { for_each_cpu(cpu, query_cpus) { /* Protected by rq_lock */ @@ -3117,6 +3411,8 @@ void sched_get_cpus_busy(struct sched_load *busy, load[i] += group_load[i]; nload[i] += ngload[i]; + + load[i] = freq_policy_load(rq, load[i]); /* * Scale load in reference to cluster max_possible_freq. * @@ -3237,6 +3533,189 @@ int sched_set_window(u64 window_start, unsigned int window_size) return 0; } +static inline void create_subtraction_entry(struct rq *rq, u64 ws, int index) +{ + rq->load_subs[index].window_start = ws; + rq->load_subs[index].subs = 0; + rq->load_subs[index].new_subs = 0; +} + +static bool get_subtraction_index(struct rq *rq, u64 ws) +{ + int i; + u64 oldest = ULLONG_MAX; + int oldest_index = 0; + + for (i = 0; i < NUM_TRACKED_WINDOWS; i++) { + u64 entry_ws = rq->load_subs[i].window_start; + + if (ws == entry_ws) + return i; + + if (entry_ws < oldest) { + oldest = entry_ws; + oldest_index = i; + } + } + + create_subtraction_entry(rq, ws, oldest_index); + return oldest_index; +} + +static void update_rq_load_subtractions(int index, struct rq *rq, + u32 sub_load, bool new_task) +{ + rq->load_subs[index].subs += sub_load; + if (new_task) + rq->load_subs[index].new_subs += sub_load; +} + +static void update_cluster_load_subtractions(struct task_struct *p, + int cpu, u64 ws, bool new_task) +{ + struct sched_cluster *cluster = cpu_cluster(cpu); + struct cpumask cluster_cpus = cluster->cpus; + u64 prev_ws = ws - sched_ravg_window; + int i; + + cpumask_clear_cpu(cpu, &cluster_cpus); + raw_spin_lock(&cluster->load_lock); + + for_each_cpu(i, &cluster_cpus) { + struct rq *rq = cpu_rq(i); + int index; + + if (p->ravg.curr_window_cpu[i]) { + index = get_subtraction_index(rq, ws); + update_rq_load_subtractions(index, rq, + p->ravg.curr_window_cpu[i], new_task); + p->ravg.curr_window_cpu[i] = 0; + } + + if (p->ravg.prev_window_cpu[i]) { + index = get_subtraction_index(rq, prev_ws); + update_rq_load_subtractions(index, rq, + p->ravg.prev_window_cpu[i], new_task); + p->ravg.prev_window_cpu[i] = 0; + } + } + + raw_spin_unlock(&cluster->load_lock); +} + +static inline void inter_cluster_migration_fixup + (struct task_struct *p, int new_cpu, int task_cpu, bool new_task) +{ + struct rq *dest_rq = cpu_rq(new_cpu); + struct rq *src_rq = cpu_rq(task_cpu); + + if (same_freq_domain(new_cpu, task_cpu)) + return; + + p->ravg.curr_window_cpu[new_cpu] = p->ravg.curr_window; + p->ravg.prev_window_cpu[new_cpu] = p->ravg.prev_window; + + dest_rq->curr_runnable_sum += p->ravg.curr_window; + dest_rq->prev_runnable_sum += p->ravg.prev_window; + + src_rq->curr_runnable_sum -= p->ravg.curr_window_cpu[task_cpu]; + src_rq->prev_runnable_sum -= p->ravg.prev_window_cpu[task_cpu]; + + if (new_task) { + dest_rq->nt_curr_runnable_sum += p->ravg.curr_window; + dest_rq->nt_prev_runnable_sum += p->ravg.prev_window; + + src_rq->nt_curr_runnable_sum -= + p->ravg.curr_window_cpu[task_cpu]; + src_rq->nt_prev_runnable_sum -= + p->ravg.prev_window_cpu[task_cpu]; + } + + p->ravg.curr_window_cpu[task_cpu] = 0; + p->ravg.prev_window_cpu[task_cpu] = 0; + + update_cluster_load_subtractions(p, task_cpu, + src_rq->window_start, new_task); + + BUG_ON((s64)src_rq->prev_runnable_sum < 0); + BUG_ON((s64)src_rq->curr_runnable_sum < 0); + BUG_ON((s64)src_rq->nt_prev_runnable_sum < 0); + BUG_ON((s64)src_rq->nt_curr_runnable_sum < 0); +} + +static int get_top_index(unsigned long *bitmap, unsigned long old_top) +{ + int index = find_next_bit(bitmap, NUM_LOAD_INDICES, old_top); + + if (index == NUM_LOAD_INDICES) + return 0; + + return NUM_LOAD_INDICES - 1 - index; +} + +static void +migrate_top_tasks(struct task_struct *p, struct rq *src_rq, struct rq *dst_rq) +{ + int index; + int top_index; + u32 curr_window = p->ravg.curr_window; + u32 prev_window = p->ravg.prev_window; + u8 src = src_rq->curr_table; + u8 dst = dst_rq->curr_table; + u8 *src_table; + u8 *dst_table; + + if (curr_window) { + src_table = src_rq->top_tasks[src]; + dst_table = dst_rq->top_tasks[dst]; + index = load_to_index(curr_window); + src_table[index] -= 1; + dst_table[index] += 1; + + if (!src_table[index]) + __clear_bit(NUM_LOAD_INDICES - index - 1, + src_rq->top_tasks_bitmap[src]); + + if (dst_table[index] == 1) + __set_bit(NUM_LOAD_INDICES - index - 1, + dst_rq->top_tasks_bitmap[dst]); + + if (index > dst_rq->curr_top) + dst_rq->curr_top = index; + + top_index = src_rq->curr_top; + if (index == top_index && !src_table[index]) + src_rq->curr_top = get_top_index( + src_rq->top_tasks_bitmap[src], top_index); + } + + if (prev_window) { + src = 1 - src; + dst = 1 - dst; + src_table = src_rq->top_tasks[src]; + dst_table = dst_rq->top_tasks[dst]; + index = load_to_index(prev_window); + src_table[index] -= 1; + dst_table[index] += 1; + + if (!src_table[index]) + __clear_bit(NUM_LOAD_INDICES - index - 1, + src_rq->top_tasks_bitmap[src]); + + if (dst_table[index] == 1) + __set_bit(NUM_LOAD_INDICES - index - 1, + dst_rq->top_tasks_bitmap[dst]); + + if (index > dst_rq->prev_top) + dst_rq->prev_top = index; + + top_index = src_rq->prev_top; + if (index == top_index && !src_table[index]) + src_rq->prev_top = get_top_index( + src_rq->top_tasks_bitmap[src], top_index); + } +} + void fixup_busy_time(struct task_struct *p, int new_cpu) { struct rq *src_rq = task_rq(p); @@ -3246,8 +3725,6 @@ void fixup_busy_time(struct task_struct *p, int new_cpu) u64 *src_prev_runnable_sum, *dst_prev_runnable_sum; u64 *src_nt_curr_runnable_sum, *dst_nt_curr_runnable_sum; u64 *src_nt_prev_runnable_sum, *dst_nt_prev_runnable_sum; - int migrate_type; - struct migration_sum_data d; bool new_task; struct related_thread_group *grp; @@ -3281,75 +3758,62 @@ void fixup_busy_time(struct task_struct *p, int new_cpu) new_task = is_new_task(p); /* Protected by rq_lock */ grp = p->grp; + + /* + * For frequency aggregation, we continue to do migration fixups + * even for intra cluster migrations. This is because, the aggregated + * load has to reported on a single CPU regardless. + */ if (grp && sched_freq_aggregate) { struct group_cpu_time *cpu_time; - migrate_type = GROUP_TO_GROUP; - /* Protected by rq_lock */ cpu_time = _group_cpu_time(grp, cpu_of(src_rq)); - d.src_rq = NULL; - d.src_cpu_time = cpu_time; src_curr_runnable_sum = &cpu_time->curr_runnable_sum; src_prev_runnable_sum = &cpu_time->prev_runnable_sum; src_nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum; src_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum; - /* Protected by rq_lock */ cpu_time = _group_cpu_time(grp, cpu_of(dest_rq)); - d.dst_rq = NULL; - d.dst_cpu_time = cpu_time; dst_curr_runnable_sum = &cpu_time->curr_runnable_sum; dst_prev_runnable_sum = &cpu_time->prev_runnable_sum; dst_nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum; dst_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum; sync_window_start(dest_rq, cpu_time); - } else { - migrate_type = RQ_TO_RQ; - d.src_rq = src_rq; - d.src_cpu_time = NULL; - d.dst_rq = dest_rq; - d.dst_cpu_time = NULL; - src_curr_runnable_sum = &src_rq->curr_runnable_sum; - src_prev_runnable_sum = &src_rq->prev_runnable_sum; - src_nt_curr_runnable_sum = &src_rq->nt_curr_runnable_sum; - src_nt_prev_runnable_sum = &src_rq->nt_prev_runnable_sum; - - dst_curr_runnable_sum = &dest_rq->curr_runnable_sum; - dst_prev_runnable_sum = &dest_rq->prev_runnable_sum; - dst_nt_curr_runnable_sum = &dest_rq->nt_curr_runnable_sum; - dst_nt_prev_runnable_sum = &dest_rq->nt_prev_runnable_sum; - } - if (p->ravg.curr_window) { - *src_curr_runnable_sum -= p->ravg.curr_window; - *dst_curr_runnable_sum += p->ravg.curr_window; - if (new_task) { - *src_nt_curr_runnable_sum -= p->ravg.curr_window; - *dst_nt_curr_runnable_sum += p->ravg.curr_window; + if (p->ravg.curr_window) { + *src_curr_runnable_sum -= p->ravg.curr_window; + *dst_curr_runnable_sum += p->ravg.curr_window; + if (new_task) { + *src_nt_curr_runnable_sum -= + p->ravg.curr_window; + *dst_nt_curr_runnable_sum += + p->ravg.curr_window; + } } - } - if (p->ravg.prev_window) { - *src_prev_runnable_sum -= p->ravg.prev_window; - *dst_prev_runnable_sum += p->ravg.prev_window; - if (new_task) { - *src_nt_prev_runnable_sum -= p->ravg.prev_window; - *dst_nt_prev_runnable_sum += p->ravg.prev_window; + if (p->ravg.prev_window) { + *src_prev_runnable_sum -= p->ravg.prev_window; + *dst_prev_runnable_sum += p->ravg.prev_window; + if (new_task) { + *src_nt_prev_runnable_sum -= + p->ravg.prev_window; + *dst_nt_prev_runnable_sum += + p->ravg.prev_window; + } } + } else { + inter_cluster_migration_fixup(p, new_cpu, + task_cpu(p), new_task); } + migrate_top_tasks(p, src_rq, dest_rq); + if (p == src_rq->ed_task) { src_rq->ed_task = NULL; if (!dest_rq->ed_task) dest_rq->ed_task = p; } - trace_sched_migration_update_sum(p, migrate_type, &d); - BUG_ON((s64)*src_prev_runnable_sum < 0); - BUG_ON((s64)*src_curr_runnable_sum < 0); - BUG_ON((s64)*src_nt_prev_runnable_sum < 0); - BUG_ON((s64)*src_nt_curr_runnable_sum < 0); - done: if (p->state == TASK_WAKING) double_rq_unlock(src_rq, dest_rq); @@ -3501,6 +3965,9 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp, u64 *src_nt_prev_runnable_sum, *dst_nt_prev_runnable_sum; struct migration_sum_data d; int migrate_type; + int cpu = cpu_of(rq); + bool new_task = is_new_task(p); + int i; if (!sched_freq_aggregate) return; @@ -3511,7 +3978,7 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp, update_task_ravg(p, rq, TASK_UPDATE, wallclock, 0); /* cpu_time protected by related_thread_group_lock, grp->lock rq_lock */ - cpu_time = _group_cpu_time(grp, cpu_of(rq)); + cpu_time = _group_cpu_time(grp, cpu); if (event == ADD_TASK) { sync_window_start(rq, cpu_time); migrate_type = RQ_TO_GROUP; @@ -3528,6 +3995,19 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp, dst_nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum; src_nt_prev_runnable_sum = &rq->nt_prev_runnable_sum; dst_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum; + + *src_curr_runnable_sum -= p->ravg.curr_window_cpu[cpu]; + *src_prev_runnable_sum -= p->ravg.prev_window_cpu[cpu]; + if (new_task) { + *src_nt_curr_runnable_sum -= + p->ravg.curr_window_cpu[cpu]; + *src_nt_prev_runnable_sum -= + p->ravg.prev_window_cpu[cpu]; + } + + update_cluster_load_subtractions(p, cpu, + rq->window_start, new_task); + } else { migrate_type = GROUP_TO_RQ; d.src_rq = NULL; @@ -3550,21 +4030,42 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp, dst_nt_curr_runnable_sum = &rq->nt_curr_runnable_sum; src_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum; dst_nt_prev_runnable_sum = &rq->nt_prev_runnable_sum; + + *src_curr_runnable_sum -= p->ravg.curr_window; + *src_prev_runnable_sum -= p->ravg.prev_window; + if (new_task) { + *src_nt_curr_runnable_sum -= p->ravg.curr_window; + *src_nt_prev_runnable_sum -= p->ravg.prev_window; + } + + /* + * Need to reset curr/prev windows for all CPUs, not just the + * ones in the same cluster. Since inter cluster migrations + * did not result in the appropriate book keeping, the values + * per CPU would be inaccurate. + */ + for_each_possible_cpu(i) { + p->ravg.curr_window_cpu[i] = 0; + p->ravg.prev_window_cpu[i] = 0; + } } - *src_curr_runnable_sum -= p->ravg.curr_window; *dst_curr_runnable_sum += p->ravg.curr_window; - - *src_prev_runnable_sum -= p->ravg.prev_window; *dst_prev_runnable_sum += p->ravg.prev_window; - - if (is_new_task(p)) { - *src_nt_curr_runnable_sum -= p->ravg.curr_window; + if (new_task) { *dst_nt_curr_runnable_sum += p->ravg.curr_window; - *src_nt_prev_runnable_sum -= p->ravg.prev_window; *dst_nt_prev_runnable_sum += p->ravg.prev_window; } + /* + * When a task enter or exits a group, it's curr and prev windows are + * moved to a single CPU. This behavior might be sub-optimal in the + * exit case, however, it saves us the overhead of handling inter + * cluster migration fixups while the task is part of a related group. + */ + p->ravg.curr_window_cpu[cpu] = p->ravg.curr_window; + p->ravg.prev_window_cpu[cpu] = p->ravg.prev_window; + trace_sched_migration_update_sum(p, migrate_type, &d); BUG_ON((s64)*src_curr_runnable_sum < 0); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 27b28369440d..471dc9faab35 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -351,13 +351,23 @@ struct cfs_bandwidth { }; #ifdef CONFIG_SCHED_HMP +#define NUM_TRACKED_WINDOWS 2 +#define NUM_LOAD_INDICES 1000 + struct hmp_sched_stats { int nr_big_tasks; u64 cumulative_runnable_avg; u64 pred_demands_sum; }; +struct load_subtractions { + u64 window_start; + u64 subs; + u64 new_subs; +}; + struct sched_cluster { + raw_spinlock_t load_lock; struct list_head list; struct cpumask cpus; int id; @@ -742,6 +752,13 @@ struct rq { u64 prev_runnable_sum; u64 nt_curr_runnable_sum; u64 nt_prev_runnable_sum; + struct load_subtractions load_subs[NUM_TRACKED_WINDOWS]; + DECLARE_BITMAP_ARRAY(top_tasks_bitmap, + NUM_TRACKED_WINDOWS, NUM_LOAD_INDICES); + u8 *top_tasks[NUM_TRACKED_WINDOWS]; + u8 curr_table; + int prev_top; + int curr_top; #endif #ifdef CONFIG_IRQ_TIME_ACCOUNTING @@ -1017,6 +1034,10 @@ static inline void sched_ttwu_pending(void) { } #define WINDOW_STATS_AVG 3 #define WINDOW_STATS_INVALID_POLICY 4 +#define FREQ_REPORT_MAX_CPU_LOAD_TOP_TASK 0 +#define FREQ_REPORT_CPU_LOAD 1 +#define FREQ_REPORT_TOP_TASK 2 + #define MAJOR_TASK_PCT 85 #define SCHED_UPMIGRATE_MIN_NICE 15 #define EXITING_TASK_MARKER 0xdeaddead @@ -1056,8 +1077,9 @@ extern unsigned int __read_mostly sched_spill_load; extern unsigned int __read_mostly sched_upmigrate; extern unsigned int __read_mostly sched_downmigrate; extern unsigned int __read_mostly sysctl_sched_spill_nr_run; +extern unsigned int __read_mostly sched_load_granule; -extern void init_new_task_load(struct task_struct *p); +extern void init_new_task_load(struct task_struct *p, bool idle_task); extern u64 sched_ktime_clock(void); extern int got_boost_kick(void); extern int register_cpu_cycle_counter_cb(struct cpu_cycle_counter_cb *cb); @@ -1401,6 +1423,7 @@ extern int cpu_upmigrate_discourage_write_u64(struct cgroup_subsys_state *css, struct cftype *cft, u64 upmigrate_discourage); extern void sched_hmp_parse_dt(void); extern void init_sched_hmp_boost_policy(void); +extern void clear_top_tasks_bitmap(unsigned long *bitmap); #else /* CONFIG_SCHED_HMP */ @@ -1503,7 +1526,9 @@ static inline struct sched_cluster *rq_cluster(struct rq *rq) return NULL; } -static inline void init_new_task_load(struct task_struct *p) { } +static inline void init_new_task_load(struct task_struct *p, bool idle_task) +{ +} static inline u64 scale_load_to_cpu(u64 load, int cpu) { @@ -1570,8 +1595,6 @@ static inline int update_preferred_cluster(struct related_thread_group *grp, static inline void add_new_task_to_grp(struct task_struct *new) {} #define sched_enable_hmp 0 -#define sched_freq_legacy_mode 1 -#define sched_migration_fixup 0 #define PRED_DEMAND_DELTA (0) static inline void diff --git a/kernel/smpboot.c b/kernel/smpboot.c index 6949476a118f..3a0415803b09 100644 --- a/kernel/smpboot.c +++ b/kernel/smpboot.c @@ -32,7 +32,7 @@ struct task_struct *idle_thread_get(unsigned int cpu) if (!tsk) return ERR_PTR(-ENOMEM); - init_idle(tsk, cpu); + init_idle(tsk, cpu, true); return tsk; } diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 587dbe09c47d..c72cb2053da7 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -297,6 +297,14 @@ static struct ctl_table kern_table[] = { }, #ifdef CONFIG_SCHED_HMP { + .procname = "sched_freq_reporting_policy", + .data = &sysctl_sched_freq_reporting_policy, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + }, + { .procname = "sched_freq_inc_notify", .data = &sysctl_sched_freq_inc_notify, .maxlen = sizeof(unsigned int), diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c index 0a9c283c250c..70be9c98b481 100644 --- a/sound/soc/codecs/wcd-spi.c +++ b/sound/soc/codecs/wcd-spi.c @@ -80,7 +80,9 @@ /* 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) + +/* Max size is closest multiple of 16 less than 64Kbytes */ +#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 16) /* Alignment requirements */ #define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT @@ -104,6 +106,12 @@ mutex_unlock(&lock); \ } +struct wcd_spi_debug_data { + struct dentry *dir; + u32 addr; + u32 size; +}; + struct wcd_spi_priv { struct spi_device *spi; u32 mem_base_addr; @@ -133,6 +141,9 @@ struct wcd_spi_priv { struct device *m_dev; struct wdsp_mgr_ops *m_ops; + + /* Debugfs related information */ + struct wcd_spi_debug_data debug_data; }; enum xfer_request { @@ -642,11 +653,10 @@ static int wcd_spi_init(struct spi_device *spi) regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG, 0x0F3D0800); - /* Write the MTU to 64K */ + /* Write the MTU to max allowed size */ regmap_update_bits(wcd_spi->regmap, WCD_SPI_SLAVE_TRNS_LEN, - 0xFFFF0000, - (WCD_SPI_RW_MULTI_MAX_LEN / 4) << 16); + 0xFFFF0000, 0xFFFF0000); err_wr_en: wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, WCD_SPI_CLK_FLAG_IMMEDIATE); @@ -1012,20 +1022,81 @@ static const struct file_operations state_fops = { .release = single_release, }; +static ssize_t wcd_spi_debugfs_mem_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct spi_device *spi = file->private_data; + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; + struct wcd_spi_msg msg; + ssize_t buf_size, read_count = 0; + char *buf; + int ret; + + if (*ppos < 0 || !count) + return -EINVAL; + + if (dbg_data->size == 0 || dbg_data->addr == 0) { + dev_err(&spi->dev, + "%s: Invalid request, size = %u, addr = 0x%x\n", + __func__, dbg_data->size, dbg_data->addr); + return 0; + } + + buf_size = count < dbg_data->size ? count : dbg_data->size; + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msg.data = buf; + msg.remote_addr = dbg_data->addr; + msg.len = buf_size; + msg.flags = 0; + + ret = wcd_spi_data_read(spi, &msg); + if (IS_ERR_VALUE(ret)) { + dev_err(&spi->dev, + "%s: Failed to read %zu bytes from addr 0x%x\n", + __func__, buf_size, msg.remote_addr); + goto done; + } + + read_count = simple_read_from_buffer(ubuf, count, ppos, buf, buf_size); + +done: + kfree(buf); + if (ret < 0) + return ret; + else + return read_count; +} + +static const struct file_operations mem_read_fops = { + .open = simple_open, + .read = wcd_spi_debugfs_mem_read, +}; + static int wcd_spi_debugfs_init(struct spi_device *spi) { + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; int rc = 0; - struct dentry *dir; - dir = debugfs_create_dir("wcd_spi", NULL); - if (IS_ERR_OR_NULL(dir)) { - dir = NULL; + dbg_data->dir = debugfs_create_dir("wcd_spi", NULL); + if (IS_ERR_OR_NULL(dbg_data->dir)) { + dbg_data->dir = NULL; rc = -ENODEV; goto done; } - debugfs_create_file("state", 0444, dir, spi, &state_fops); + debugfs_create_file("state", 0444, dbg_data->dir, spi, &state_fops); + debugfs_create_u32("addr", S_IRUGO | S_IWUSR, dbg_data->dir, + &dbg_data->addr); + debugfs_create_u32("size", S_IRUGO | S_IWUSR, dbg_data->dir, + &dbg_data->size); + debugfs_create_file("mem_read", S_IRUGO, dbg_data->dir, + spi, &mem_read_fops); done: return rc; } diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 9394ee52cad0..b2d4e08a55cc 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -842,6 +842,9 @@ struct tasha_priv { int rx_8_count; bool clk_mode; bool clk_internal; + + /* Lock to protect mclk enablement */ + struct mutex mclk_lock; }; static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, @@ -1152,13 +1155,14 @@ static int tasha_cdc_req_mclk_enable(struct tasha_priv *tasha, { int ret = 0; + mutex_lock(&tasha->mclk_lock); if (enable) { tasha_cdc_sido_ccl_enable(tasha, true); ret = clk_prepare_enable(tasha->wcd_ext_clk); if (ret) { dev_err(tasha->dev, "%s: ext clk enable failed\n", __func__); - goto err; + goto unlock_mutex; } /* get BG */ wcd_resmgr_enable_master_bias(tasha->resmgr); @@ -1172,7 +1176,8 @@ static int tasha_cdc_req_mclk_enable(struct tasha_priv *tasha, clk_disable_unprepare(tasha->wcd_ext_clk); tasha_cdc_sido_ccl_enable(tasha, false); } -err: +unlock_mutex: + mutex_unlock(&tasha->mclk_lock); return ret; } @@ -14114,6 +14119,7 @@ static int tasha_probe(struct platform_device *pdev) mutex_init(&tasha->swr_read_lock); mutex_init(&tasha->swr_write_lock); mutex_init(&tasha->swr_clk_lock); + mutex_init(&tasha->mclk_lock); cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region), GFP_KERNEL); @@ -14199,6 +14205,7 @@ err_clk: err_resmgr: devm_kfree(&pdev->dev, cdc_pwr); err_cdc_pwr: + mutex_destroy(&tasha->mclk_lock); devm_kfree(&pdev->dev, tasha); return ret; } @@ -14213,6 +14220,7 @@ static int tasha_remove(struct platform_device *pdev) clk_put(tasha->wcd_ext_clk); if (tasha->wcd_native_clk) clk_put(tasha->wcd_native_clk); + mutex_destroy(&tasha->mclk_lock); devm_kfree(&pdev->dev, tasha); snd_soc_unregister_codec(&pdev->dev); return 0; |
