diff options
39 files changed, 690 insertions, 416 deletions
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index 0b46fd3d8ebf..d00e26b4d5ed 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -75,6 +75,11 @@ Optional Properties: during clock scaling. If this property is not defined, then it falls back to the default HS bus speed mode to maintain backward compatibility. + - qcom,sdr104-wa: On Certain chipsets, SDR104 mode might be unstable causing CRC errors + on the interface. So there is a workaround implemented to skip printing + register dumps on CRC errors and also downgrade bus speed mode to + SDR50/DDR50 in case of continuous CRC errors. Set this flag to enable + this workaround. In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage). - qcom,<supply>-always-on - specifies whether supply should be kept "on" always. diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi index 03801ee90589..3888047b9f8c 100644 --- a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-itech-3000mah.dtsi @@ -14,7 +14,7 @@ qcom,itech_3000mah { /* #Itech_B00826LF_3000mAh_ver1660_averaged_MasterSlave_Jan10th2017*/ qcom,max-voltage-uv = <4350000>; qcom,fg-cc-cv-threshold-mv = <4340>; - qcom,fastchg-current-ma = <3000>; + qcom,fastchg-current-ma = <2000>; qcom,batt-id-kohm = <100>; qcom,battery-beta = <3435>; qcom,battery-type = "itech_b00826lf_3000mah_ver1660_jan10th2017"; diff --git a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-qrd-skuk-4v4-3000mah.dtsi b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-qrd-skuk-4v4-3000mah.dtsi index 8cbb29aac927..11600ef2bfd3 100644 --- a/arch/arm/boot/dts/qcom/fg-gen3-batterydata-qrd-skuk-4v4-3000mah.dtsi +++ b/arch/arm/boot/dts/qcom/fg-gen3-batterydata-qrd-skuk-4v4-3000mah.dtsi @@ -13,6 +13,7 @@ qcom,qrd_msm8998_skuk_3000mah { /* QRD8997_ST1031GA_3000mAh_averaged_MasterSlave_Jan10th2017 */ qcom,max-voltage-uv = <4400000>; + qcom,fg-cc-cv-threshold-mv = <4390>; qcom,fastchg-current-ma = <3000>; qcom,batt-id-kohm = <68>; qcom,battery-beta = <3380>; diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 02b7a44ee0d2..d1194b3ffcec 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -1575,6 +1575,8 @@ qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000 100000000 200000000 4294967295>; + qcom,sdr104-wa; + status = "disabled"; }; diff --git a/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi b/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi index 9ac76f7e2f6e..d35704224f45 100644 --- a/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630-mdss.dtsi @@ -38,8 +38,8 @@ qcom,mdss-ib-factor = <1 1>; /* 1 time */ qcom,mdss-clk-factor = <105 100>; /* 1.05 times */ - qcom,max-mixer-width = <2560>; - qcom,max-pipe-width = <2560>; + qcom,max-mixer-width = <2048>; + qcom,max-pipe-width = <2048>; qcom,max-dest-scaler-input-width = <2048>; qcom,max-dest-scaler-output-width = <2560>; diff --git a/arch/arm/boot/dts/qcom/sdm630.dtsi b/arch/arm/boot/dts/qcom/sdm630.dtsi index 9626e0548789..67e899d8ba5e 100644 --- a/arch/arm/boot/dts/qcom/sdm630.dtsi +++ b/arch/arm/boot/dts/qcom/sdm630.dtsi @@ -1628,6 +1628,8 @@ reg-names = "membase", "smmu_iova_base", "smmu_iova_ipa"; iommus = <&anoc2_smmu 0x1a00>, <&anoc2_smmu 0x1a01>; + clocks = <&clock_rpmcc RPM_RF_CLK1_PIN>; + clock-names = "cxo_ref_clk_pin"; interrupts = <0 413 0>, /* CE0 */ <0 414 0>, /* CE1 */ <0 415 0>, /* CE2 */ @@ -1640,6 +1642,14 @@ <0 423 0>, /* CE9 */ <0 424 0>, /* CE10 */ <0 425 0>; /* CE11 */ + vdd-0.8-cx-mx-supply = <&pm660_l5>; + vdd-1.8-xo-supply = <&pm660_l9_pin_ctrl>; + vdd-1.3-rfa-supply = <&pm660_l6_pin_ctrl>; + vdd-3.3-ch0-supply = <&pm660_l19_pin_ctrl>; + qcom,vdd-0.8-cx-mx-config = <525000 950000>; + qcom,vdd-1.8-xo-config = <1750000 1900000>; + qcom,vdd-1.3-rfa-config = <1200000 1370000>; + qcom,vdd-3.3-ch0-config = <3200000 3400000>; qcom,wlan-msa-memory = <0x100000>; qcom,smmu-s1-bypass; }; diff --git a/arch/arm/boot/dts/qcom/sdm660.dtsi b/arch/arm/boot/dts/qcom/sdm660.dtsi index 1e0b6136e1b4..be200f8dd531 100644 --- a/arch/arm/boot/dts/qcom/sdm660.dtsi +++ b/arch/arm/boot/dts/qcom/sdm660.dtsi @@ -1879,6 +1879,8 @@ reg-names = "membase", "smmu_iova_base", "smmu_iova_ipa"; iommus = <&anoc2_smmu 0x1a00>, <&anoc2_smmu 0x1a01>; + clocks = <&clock_rpmcc RPM_RF_CLK1_PIN>; + clock-names = "cxo_ref_clk_pin"; interrupts = <0 413 0>, /* CE0 */ <0 414 0>, /* CE1 */ <0 415 0>, /* CE2 */ @@ -1891,6 +1893,14 @@ <0 423 0>, /* CE9 */ <0 424 0>, /* CE10 */ <0 425 0>; /* CE11 */ + vdd-0.8-cx-mx-supply = <&pm660_l5>; + vdd-1.8-xo-supply = <&pm660_l9_pin_ctrl>; + vdd-1.3-rfa-supply = <&pm660_l6_pin_ctrl>; + vdd-3.3-ch0-supply = <&pm660_l19_pin_ctrl>; + qcom,vdd-0.8-cx-mx-config = <525000 950000>; + qcom,vdd-1.8-xo-config = <1750000 1900000>; + qcom,vdd-1.3-rfa-config = <1200000 1370000>; + qcom,vdd-3.3-ch0-config = <3200000 3400000>; qcom,wlan-msa-memory = <0x100000>; qcom,smmu-s1-bypass; }; diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index 8dd03e6695cb..479599473381 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -553,7 +553,7 @@ static void fastrpc_mmap_free(struct fastrpc_mmap *map) if (!IS_ERR_OR_NULL(map->handle)) ion_free(fl->apps->client, map->handle); - if (sess->smmu.enabled) { + if (sess && sess->smmu.enabled) { if (map->size || map->phys) msm_dma_unmap_sg(sess->smmu.dev, map->table->sgl, @@ -645,6 +645,9 @@ static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd, unsigned attr, else sess = fl->sctx; + VERIFY(err, !IS_ERR_OR_NULL(sess)); + if (err) + goto bail; VERIFY(err, !IS_ERR_OR_NULL(map->buf = dma_buf_get(fd))); if (err) goto bail; diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c index ab21334d5813..4002a5b57250 100644 --- a/drivers/crypto/msm/ice.c +++ b/drivers/crypto/msm/ice.c @@ -1608,7 +1608,8 @@ static struct ice_device *get_ice_device_from_storage_type list_for_each_entry(ice_dev, &ice_devices, list) { if (!strcmp(ice_dev->ice_instance_type, storage_type)) { - pr_info("%s: found ice device %p\n", __func__, ice_dev); + pr_debug("%s: found ice device %pK\n", + __func__, ice_dev); break; } } diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c index 161b718b8a38..d79d9613043f 100644 --- a/drivers/gpu/msm/adreno_ringbuffer.c +++ b/drivers/gpu/msm/adreno_ringbuffer.c @@ -813,10 +813,10 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev, dwords += 6; /* - * REG_TO_MEM packet on A5xx needs another ordinal. + * REG_TO_MEM packet on A5xx and above needs another ordinal. * Add 2 more dwords since we do profiling before and after. */ - if (adreno_is_a5xx(adreno_dev)) + if (!ADRENO_LEGACY_PM4(adreno_dev)) dwords += 2; /* @@ -833,7 +833,7 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev, if (test_bit(CMDOBJ_PROFILE, &cmdobj->priv)) { kernel_profiling = true; dwords += 6; - if (adreno_is_a5xx(adreno_dev)) + if (!ADRENO_LEGACY_PM4(adreno_dev)) dwords += 2; } diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c index 54d395d5e78d..a8bf5b4b24c5 100644 --- a/drivers/leds/leds-qpnp-flash-v2.c +++ b/drivers/leds/leds-qpnp-flash-v2.c @@ -509,7 +509,7 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) if (led->pdata->led1n2_iclamp_low_ma) { val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_low_ma, - led->fnode[0].ires_ua); + led->fnode[LED1].ires_ua); rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_LED1N2_ICLAMP_LOW(led->base), FLASH_LED_CURRENT_MASK, val); @@ -519,7 +519,7 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) if (led->pdata->led1n2_iclamp_mid_ma) { val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_mid_ma, - led->fnode[0].ires_ua); + led->fnode[LED1].ires_ua); rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_LED1N2_ICLAMP_MID(led->base), FLASH_LED_CURRENT_MASK, val); @@ -529,7 +529,7 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) if (led->pdata->led3_iclamp_low_ma) { val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_low_ma, - led->fnode[3].ires_ua); + led->fnode[LED3].ires_ua); rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_LED3_ICLAMP_LOW(led->base), FLASH_LED_CURRENT_MASK, val); @@ -539,7 +539,7 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led) if (led->pdata->led3_iclamp_mid_ma) { val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_mid_ma, - led->fnode[3].ires_ua); + led->fnode[LED3].ires_ua); rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_LED3_ICLAMP_MID(led->base), FLASH_LED_CURRENT_MASK, val); @@ -2094,22 +2094,24 @@ static int qpnp_flash_led_probe(struct platform_device *pdev) if (!strcmp("flash", temp_string) || !strcmp("torch", temp_string)) { rc = qpnp_flash_led_parse_each_led_dt(led, - &led->fnode[i++], temp); + &led->fnode[i], temp); if (rc < 0) { pr_err("Unable to parse flash node %d rc=%d\n", i, rc); goto error_led_register; } + i++; } if (!strcmp("switch", temp_string)) { rc = qpnp_flash_led_parse_and_register_switch(led, - &led->snode[j++], temp); + &led->snode[j], temp); if (rc < 0) { pr_err("Unable to parse and register switch node, rc=%d\n", rc); goto error_switch_register; } + j++; } } diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5d6c44b00bc2..e9f1a19dfe3f 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2189,6 +2189,17 @@ static int mmc_blk_err_check(struct mmc_card *card, int need_retune = card->host->need_retune; int ecc_err = 0, gen_err = 0; + if (card->host->sdr104_wa && mmc_card_sd(card) && + (card->host->ios.timing == MMC_TIMING_UHS_SDR104) && + !card->sdr104_blocked && + (brq->data.error == -EILSEQ || + brq->data.error == -EIO || + brq->data.error == -ETIMEDOUT || + brq->cmd.error == -EILSEQ || + brq->cmd.error == -EIO || + brq->cmd.error == -ETIMEDOUT)) + card->err_in_sdr104 = true; + /* * sbc.error indicates a problem with the set block count * command. No data will have been transferred. @@ -3645,6 +3656,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) struct mmc_async_req *areq; const u8 packed_nr = 2; u8 reqs = 0; + bool reset = false; #ifdef CONFIG_MMC_SIMULATE_MAX_SPEED unsigned long waitfor = jiffies; #endif @@ -3690,6 +3702,26 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) type = rq_data_dir(req) == READ ? MMC_BLK_READ : MMC_BLK_WRITE; mmc_queue_bounce_post(mq_rq); + if (card->err_in_sdr104) { + /* + * Data CRC/timeout errors will manifest as CMD/DATA + * ERR. But we'd like to retry these too. + * Moreover, no harm done if this fails too for multiple + * times, we anyway reduce the bus-speed and retry the + * same request. + * If that fails too, we don't override this status. + */ + if (status == MMC_BLK_ABORT || + status == MMC_BLK_CMD_ERR || + status == MMC_BLK_DATA_ERR || + status == MMC_BLK_RETRY) + /* reset on all of these errors and retry */ + reset = true; + + status = MMC_BLK_RETRY; + card->err_in_sdr104 = false; + } + switch (status) { case MMC_BLK_SUCCESS: case MMC_BLK_PARTIAL: @@ -3730,8 +3762,32 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) break; case MMC_BLK_RETRY: retune_retry_done = brq->retune_retry_done; - if (retry++ < MMC_BLK_MAX_RETRIES) + if (retry++ < MMC_BLK_MAX_RETRIES) { break; + } else if (reset) { + reset = false; + /* + * If we exhaust all the retries due to + * CRC/timeout errors in SDR140 mode with UHS SD + * cards, re-configure the card in SDR50 + * bus-speed mode. + * All subsequent re-init of this card will be + * in SDR50 mode, unless it is removed and + * re-inserted. When new UHS SD cards are + * inserted, it may start at SDR104 mode if + * supported by the card. + */ + pr_err("%s: blocked SDR104, lower the bus-speed (SDR50 / DDR50)\n", + req->rq_disk->disk_name); + mmc_host_clear_sdr104(card->host); + mmc_suspend_clk_scaling(card->host); + mmc_blk_reset(md, card->host, type); + /* SDR104 mode is blocked from now on */ + card->sdr104_blocked = true; + /* retry 5 times again */ + retry = 0; + break; + } /* Fall through */ case MMC_BLK_ABORT: if (!mmc_blk_reset(md, card->host, type) && diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index c409f713d4f0..41f0935440fd 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -4040,6 +4040,10 @@ int _mmc_detect_card_removed(struct mmc_host *host) if (ret) { mmc_card_set_removed(host->card); + if (host->card->sdr104_blocked) { + mmc_host_set_sdr104(host); + host->card->sdr104_blocked = false; + } pr_debug("%s: card remove detected\n", mmc_hostname(host)); } diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 1116544eebc1..c66187299598 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -80,7 +80,6 @@ void mmc_init_context_info(struct mmc_host *host); extern bool mmc_can_scale_clk(struct mmc_host *host); extern int mmc_init_clk_scaling(struct mmc_host *host); -extern int mmc_suspend_clk_scaling(struct mmc_host *host); extern int mmc_resume_clk_scaling(struct mmc_host *host); extern int mmc_exit_clk_scaling(struct mmc_host *host); extern unsigned long mmc_get_max_frequency(struct mmc_host *host); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 9e147a667edf..5b4d5d74fe55 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1285,6 +1285,8 @@ static int _mmc_sd_resume(struct mmc_host *host) #endif mmc_card_clr_suspended(host->card); + if (host->card->sdr104_blocked) + goto out; err = mmc_resume_clk_scaling(host); if (err) { pr_err("%s: %s: fail to resume clock scaling (%d)\n", diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 2eaac11ec8ba..987d61bdda2d 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1960,6 +1960,8 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, if (of_get_property(np, "qcom,core_3_0v_support", NULL)) pdata->core_3_0v_support = true; + pdata->sdr104_wa = of_property_read_bool(np, "qcom,sdr104-wa"); + return pdata; out: return NULL; @@ -4579,6 +4581,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (msm_host->pdata->nonhotplug) msm_host->mmc->caps2 |= MMC_CAP2_NONHOTPLUG; + msm_host->mmc->sdr104_wa = msm_host->pdata->sdr104_wa; /* Initialize ICE if present */ if (msm_host->ice.pdev) { diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h index 2e4f2179378e..92f61708001e 100644 --- a/drivers/mmc/host/sdhci-msm.h +++ b/drivers/mmc/host/sdhci-msm.h @@ -153,6 +153,7 @@ struct sdhci_msm_pltfm_data { u32 ice_clk_min; struct sdhci_msm_pm_qos_data pm_qos_data; bool core_3_0v_support; + bool sdr104_wa; }; struct sdhci_msm_bus_vote { diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 44633dc5d2be..40a34c283955 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3083,7 +3083,10 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) mmc_hostname(host->mmc), intmask, host->data->error, ktime_to_ms(ktime_sub( ktime_get(), host->data_start_time))); - sdhci_dumpregs(host); + + if (!host->mmc->sdr104_wa || + (host->mmc->ios.timing != MMC_TIMING_UHS_SDR104)) + sdhci_dumpregs(host); } sdhci_finish_data(host); } else { diff --git a/drivers/net/ethernet/msm/msm_rmnet_mhi.c b/drivers/net/ethernet/msm/msm_rmnet_mhi.c index bf6502e27bdd..015cb99d445b 100644 --- a/drivers/net/ethernet/msm/msm_rmnet_mhi.c +++ b/drivers/net/ethernet/msm/msm_rmnet_mhi.c @@ -35,6 +35,7 @@ #define MHI_NAPI_WEIGHT_VALUE 12 #define WATCHDOG_TIMEOUT (30 * HZ) #define RMNET_IPC_LOG_PAGES (100) +#define IRQ_MASKED_BIT (0) enum DBG_LVL { MSG_VERBOSE = 0x1, @@ -100,14 +101,15 @@ struct rmnet_mhi_private { u32 mhi_enabled; struct platform_device *pdev; struct net_device *dev; - atomic_t irq_masked_cntr; + unsigned long flags; + int wake_count; spinlock_t out_chan_full_lock; /* tx queue lock */ - atomic_t pending_data; struct sk_buff *frag_skb; struct work_struct alloc_work; /* lock to queue hardware and internal queue */ spinlock_t alloc_lock; void *rmnet_ipc_log; + rwlock_t pm_lock; /* state change lock */ struct debug_params debug; struct dentry *dentry; }; @@ -130,12 +132,12 @@ static int rmnet_mhi_process_fragment(struct rmnet_mhi_private *rmnet_mhi_ptr, rmnet_mhi_ptr->frag_skb = NULL; return -ENOMEM; } - kfree_skb(rmnet_mhi_ptr->frag_skb); + dev_kfree_skb_any(rmnet_mhi_ptr->frag_skb); rmnet_mhi_ptr->frag_skb = temp_skb; memcpy(skb_put(rmnet_mhi_ptr->frag_skb, skb->len), skb->data, skb->len); - kfree_skb(skb); + dev_kfree_skb_any(skb); if (!frag) { /* Last fragmented piece was received, ship it */ netif_receive_skb(rmnet_mhi_ptr->frag_skb); @@ -196,7 +198,6 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr, { u32 cur_mru = rmnet_mhi_ptr->mru; struct mhi_skb_priv *skb_priv; - unsigned long flags; int ret; struct sk_buff *skb; @@ -215,7 +216,7 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr, skb_priv->dma_addr = 0; /* These steps must be in atomic context */ - spin_lock_irqsave(&rmnet_mhi_ptr->alloc_lock, flags); + spin_lock_bh(&rmnet_mhi_ptr->alloc_lock); /* It's possible by the time alloc_skb (GFP_KERNEL) * returns we already called rmnet_alloc_rx @@ -224,14 +225,22 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr, */ if (unlikely(atomic_read(&rmnet_mhi_ptr->rx_pool_len) >= rmnet_mhi_ptr->rx_buffers_max)) { - spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock, - flags); + spin_unlock_bh(&rmnet_mhi_ptr->alloc_lock); dev_kfree_skb_any(skb); return 0; } - ret = mhi_queue_xfer( - rmnet_mhi_ptr->rx_client_handle, + read_lock_bh(&rmnet_mhi_ptr->pm_lock); + if (unlikely(!rmnet_mhi_ptr->mhi_enabled)) { + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "!interface is disabled\n"); + dev_kfree_skb_any(skb); + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); + spin_unlock_bh(&rmnet_mhi_ptr->alloc_lock); + return -EIO; + } + + ret = mhi_queue_xfer(rmnet_mhi_ptr->rx_client_handle, skb->data, skb_priv->dma_size, MHI_EOT); @@ -239,14 +248,15 @@ static int rmnet_alloc_rx(struct rmnet_mhi_private *rmnet_mhi_ptr, rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, "mhi_queue_xfer failed, error %d", ret); - spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock, - flags); + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); + spin_unlock_bh(&rmnet_mhi_ptr->alloc_lock); dev_kfree_skb_any(skb); return ret; } skb_queue_tail(&rmnet_mhi_ptr->rx_buffers, skb); atomic_inc(&rmnet_mhi_ptr->rx_pool_len); - spin_unlock_irqrestore(&rmnet_mhi_ptr->alloc_lock, flags); + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); + spin_unlock_bh(&rmnet_mhi_ptr->alloc_lock); } return 0; @@ -258,13 +268,25 @@ static void rmnet_mhi_alloc_work(struct work_struct *work) struct rmnet_mhi_private, alloc_work); int ret; + /* sleep about 1 sec and retry, that should be enough time + * for system to reclaim freed memory back. + */ + const int sleep_ms = 1000; + int retry = 60; rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n"); - ret = rmnet_alloc_rx(rmnet_mhi_ptr, - rmnet_mhi_ptr->allocation_flags); + do { + ret = rmnet_alloc_rx(rmnet_mhi_ptr, + rmnet_mhi_ptr->allocation_flags); + /* sleep and try again */ + if (ret == -ENOMEM) { + msleep(sleep_ms); + retry--; + } + } while (ret == -ENOMEM && retry); - WARN_ON(ret == -ENOMEM); - rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exit\n"); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exit with status:%d retry:%d\n", + ret, retry); } static int rmnet_mhi_poll(struct napi_struct *napi, int budget) @@ -281,6 +303,12 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n"); + read_lock_bh(&rmnet_mhi_ptr->pm_lock); + if (unlikely(!rmnet_mhi_ptr->mhi_enabled)) { + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "interface is disabled!\n"); + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); + return 0; + } while (received_packets < budget) { struct mhi_result *result = mhi_poll(rmnet_mhi_ptr->rx_client_handle); @@ -338,77 +366,50 @@ static int rmnet_mhi_poll(struct napi_struct *napi, int budget) dev->stats.rx_bytes += result->bytes_xferd; } /* while (received_packets < budget) or any other error */ + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); /* Queue new buffers */ res = rmnet_alloc_rx(rmnet_mhi_ptr, GFP_ATOMIC); - if (res == -ENOMEM) { - rmnet_log(rmnet_mhi_ptr, - MSG_INFO, - "out of mem, queuing bg worker\n"); - rmnet_mhi_ptr->alloc_fail++; - schedule_work(&rmnet_mhi_ptr->alloc_work); - } - napi_complete(napi); + read_lock_bh(&rmnet_mhi_ptr->pm_lock); + if (likely(rmnet_mhi_ptr->mhi_enabled)) { + if (res == -ENOMEM) { + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "out of mem, queuing bg worker\n"); + rmnet_mhi_ptr->alloc_fail++; + schedule_work(&rmnet_mhi_ptr->alloc_work); + } + + napi_complete(napi); - /* We got a NULL descriptor back */ - if (should_reschedule == false) { - if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) { - atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr); - mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle); + /* We got a NULL descriptor back */ + if (!should_reschedule) { + if (test_and_clear_bit(IRQ_MASKED_BIT, + &rmnet_mhi_ptr->flags)) + mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle); mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, true); + rmnet_mhi_ptr->wake_count--; + } else { + if (received_packets == budget) + rmnet_mhi_ptr->debug.rx_napi_budget_overflow++; + napi_reschedule(napi); } - } else { - if (received_packets == budget) - rmnet_mhi_ptr->debug.rx_napi_budget_overflow++; - napi_reschedule(napi); - } - rmnet_mhi_ptr->debug.rx_napi_skb_burst_min = - min((u64)received_packets, - rmnet_mhi_ptr->debug.rx_napi_skb_burst_min); + rmnet_mhi_ptr->debug.rx_napi_skb_burst_min = + min((u64)received_packets, + rmnet_mhi_ptr->debug.rx_napi_skb_burst_min); - rmnet_mhi_ptr->debug.rx_napi_skb_burst_max = - max((u64)received_packets, - rmnet_mhi_ptr->debug.rx_napi_skb_burst_max); + rmnet_mhi_ptr->debug.rx_napi_skb_burst_max = + max((u64)received_packets, + rmnet_mhi_ptr->debug.rx_napi_skb_burst_max); + } + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited, polled %d pkts\n", received_packets); return received_packets; } -void rmnet_mhi_clean_buffers(struct net_device *dev) -{ - struct rmnet_mhi_private *rmnet_mhi_ptr = - *(struct rmnet_mhi_private **)netdev_priv(dev); - - rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Entered\n"); - /* Clean TX buffers */ - rmnet_mhi_internal_clean_unmap_buffers(dev, - &rmnet_mhi_ptr->tx_buffers, - DMA_TO_DEVICE); - - /* Clean RX buffers */ - rmnet_mhi_internal_clean_unmap_buffers(dev, - &rmnet_mhi_ptr->rx_buffers, - DMA_FROM_DEVICE); - rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited\n"); -} - -static int rmnet_mhi_disable_channels(struct rmnet_mhi_private *rmnet_mhi_ptr) -{ - rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI TX channel\n"); - mhi_close_channel(rmnet_mhi_ptr->tx_client_handle); - rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Closing MHI RX channel\n"); - mhi_close_channel(rmnet_mhi_ptr->rx_client_handle); - rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Clearing Pending TX buffers.\n"); - rmnet_mhi_clean_buffers(rmnet_mhi_ptr->dev); - rmnet_mhi_ptr->tx_client_handle = NULL; - rmnet_mhi_ptr->rx_client_handle = NULL; - - return 0; -} - static int rmnet_mhi_init_inbound(struct rmnet_mhi_private *rmnet_mhi_ptr) { int res; @@ -431,7 +432,7 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result) struct net_device *dev; struct rmnet_mhi_private *rmnet_mhi_ptr; unsigned long burst_counter = 0; - unsigned long flags; + unsigned long flags, pm_flags; rmnet_mhi_ptr = result->user_data; dev = rmnet_mhi_ptr->dev; @@ -451,10 +452,10 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result) break; } else { if (skb->data == result->buf_addr) { - kfree_skb(skb); + dev_kfree_skb_any(skb); break; } - kfree_skb(skb); + dev_kfree_skb_any(skb); burst_counter++; /* Update statistics */ @@ -477,10 +478,15 @@ static void rmnet_mhi_tx_cb(struct mhi_result *result) rmnet_mhi_ptr->debug.tx_cb_skb_free_burst_max); /* In case we couldn't write again, now we can! */ - spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags); - rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Waking up queue\n"); - netif_wake_queue(dev); - spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags); + read_lock_irqsave(&rmnet_mhi_ptr->pm_lock, pm_flags); + if (likely(rmnet_mhi_ptr->mhi_enabled)) { + spin_lock_irqsave(&rmnet_mhi_ptr->out_chan_full_lock, flags); + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Waking up queue\n"); + netif_wake_queue(dev); + spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, + flags); + } + read_unlock_irqrestore(&rmnet_mhi_ptr->pm_lock, pm_flags); rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n"); } @@ -488,20 +494,27 @@ static void rmnet_mhi_rx_cb(struct mhi_result *result) { struct net_device *dev; struct rmnet_mhi_private *rmnet_mhi_ptr; + unsigned long flags; + rmnet_mhi_ptr = result->user_data; dev = rmnet_mhi_ptr->dev; rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n"); rmnet_mhi_ptr->debug.rx_interrupts_count++; - - if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) { - mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle); - atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr); - mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false); - __napi_schedule(&(rmnet_mhi_ptr->napi)); - } else { - rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++; + read_lock_irqsave(&rmnet_mhi_ptr->pm_lock, flags); + if (likely(rmnet_mhi_ptr->mhi_enabled)) { + if (napi_schedule_prep(&rmnet_mhi_ptr->napi)) { + if (!test_and_set_bit(IRQ_MASKED_BIT, + &rmnet_mhi_ptr->flags)) + mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle); + mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false); + rmnet_mhi_ptr->wake_count++; + __napi_schedule(&rmnet_mhi_ptr->napi); + } else { + rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++; + } } + read_unlock_irqrestore(&rmnet_mhi_ptr->pm_lock, flags); rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n"); } @@ -510,8 +523,7 @@ static int rmnet_mhi_open(struct net_device *dev) struct rmnet_mhi_private *rmnet_mhi_ptr = *(struct rmnet_mhi_private **)netdev_priv(dev); - rmnet_log(rmnet_mhi_ptr, - MSG_INFO, + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Opened net dev interface for MHI chans %d and %d\n", rmnet_mhi_ptr->tx_channel, rmnet_mhi_ptr->rx_channel); @@ -527,43 +539,35 @@ static int rmnet_mhi_open(struct net_device *dev) /* Poll to check if any buffers are accumulated in the * transport buffers */ - if (napi_schedule_prep(&(rmnet_mhi_ptr->napi))) { - mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle); - atomic_inc(&rmnet_mhi_ptr->irq_masked_cntr); - mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false); - __napi_schedule(&(rmnet_mhi_ptr->napi)); - } else { - rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++; + read_lock_bh(&rmnet_mhi_ptr->pm_lock); + if (likely(rmnet_mhi_ptr->mhi_enabled)) { + if (napi_schedule_prep(&rmnet_mhi_ptr->napi)) { + if (!test_and_set_bit(IRQ_MASKED_BIT, + &rmnet_mhi_ptr->flags)) { + mhi_mask_irq(rmnet_mhi_ptr->rx_client_handle); + } + mhi_set_lpm(rmnet_mhi_ptr->rx_client_handle, false); + rmnet_mhi_ptr->wake_count++; + __napi_schedule(&rmnet_mhi_ptr->napi); + } else { + rmnet_mhi_ptr->debug.rx_interrupts_in_masked_irq++; + } } + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); return 0; } -static int rmnet_mhi_disable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) -{ - rmnet_mhi_ptr->rx_enabled = 0; - rmnet_mhi_ptr->tx_enabled = 0; - rmnet_mhi_ptr->mhi_enabled = 0; - if (rmnet_mhi_ptr->dev != 0) { - netif_stop_queue(rmnet_mhi_ptr->dev); - netif_napi_del(&(rmnet_mhi_ptr->napi)); - rmnet_mhi_disable_channels(rmnet_mhi_ptr); - unregister_netdev(rmnet_mhi_ptr->dev); - free_netdev(rmnet_mhi_ptr->dev); - rmnet_mhi_ptr->dev = 0; - } - return 0; -} - static int rmnet_mhi_disable(struct rmnet_mhi_private *rmnet_mhi_ptr) { - rmnet_mhi_ptr->mhi_enabled = 0; - rmnet_mhi_disable_iface(rmnet_mhi_ptr); napi_disable(&(rmnet_mhi_ptr->napi)); - if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) { + rmnet_mhi_ptr->rx_enabled = 0; + rmnet_mhi_internal_clean_unmap_buffers(rmnet_mhi_ptr->dev, + &rmnet_mhi_ptr->rx_buffers, + DMA_FROM_DEVICE); + if (test_and_clear_bit(IRQ_MASKED_BIT, &rmnet_mhi_ptr->flags)) mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle); - atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr); - } + return 0; } @@ -574,11 +578,9 @@ static int rmnet_mhi_stop(struct net_device *dev) netif_stop_queue(dev); rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Entered\n"); - if (atomic_read(&rmnet_mhi_ptr->irq_masked_cntr)) { + if (test_and_clear_bit(IRQ_MASKED_BIT, &rmnet_mhi_ptr->flags)) { mhi_unmask_irq(rmnet_mhi_ptr->rx_client_handle); - atomic_dec(&rmnet_mhi_ptr->irq_masked_cntr); - rmnet_log(rmnet_mhi_ptr, - MSG_ERROR, + rmnet_log(rmnet_mhi_ptr, MSG_ERROR, "IRQ was masked, unmasking...\n"); } rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n"); @@ -605,14 +607,23 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev) unsigned long flags; struct mhi_skb_priv *tx_priv; - rmnet_log(rmnet_mhi_ptr, - MSG_VERBOSE, - "Entered chan %d\n", - rmnet_mhi_ptr->tx_channel); + rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, + "Entered chan %d\n", rmnet_mhi_ptr->tx_channel); tx_priv = (struct mhi_skb_priv *)(skb->cb); tx_priv->dma_size = skb->len; tx_priv->dma_addr = 0; + read_lock_bh(&rmnet_mhi_ptr->pm_lock); + if (unlikely(!rmnet_mhi_ptr->mhi_enabled)) { + /* Only reason interface could be disabled and we get data + * is due to an SSR. We do not want to stop the queue and + * return error. instead we will flush all the uplink packets + * and return successful + */ + res = NETDEV_TX_OK; + dev_kfree_skb_any(skb); + goto mhi_xmit_exit; + } if (mhi_get_free_desc(rmnet_mhi_ptr->tx_client_handle) <= 0) { rmnet_log(rmnet_mhi_ptr, @@ -624,7 +635,8 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags); - return NETDEV_TX_BUSY; + res = NETDEV_TX_BUSY; + goto mhi_xmit_exit; } res = mhi_queue_xfer(rmnet_mhi_ptr->tx_client_handle, skb->data, @@ -641,15 +653,17 @@ static int rmnet_mhi_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); spin_unlock_irqrestore(&rmnet_mhi_ptr->out_chan_full_lock, flags); - return NETDEV_TX_BUSY; + res = NETDEV_TX_BUSY; + goto mhi_xmit_exit; } - + res = NETDEV_TX_OK; skb_queue_tail(&(rmnet_mhi_ptr->tx_buffers), skb); dev->trans_start = jiffies; rmnet_mhi_ptr->debug.tx_queued_packets_count++; - +mhi_xmit_exit: + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); rmnet_log(rmnet_mhi_ptr, MSG_VERBOSE, "Exited\n"); - return NETDEV_TX_OK; + return res; } static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) @@ -698,16 +712,19 @@ static int rmnet_mhi_ioctl_extended(struct net_device *dev, struct ifreq *ifr) sizeof(ext_cmd.u.if_name)); break; case RMNET_IOCTL_SET_SLEEP_STATE: + read_lock_bh(&rmnet_mhi_ptr->pm_lock); if (rmnet_mhi_ptr->mhi_enabled && rmnet_mhi_ptr->tx_client_handle != NULL) { + rmnet_mhi_ptr->wake_count += (ext_cmd.u.data) ? -1 : 1; mhi_set_lpm(rmnet_mhi_ptr->tx_client_handle, ext_cmd.u.data); } else { - rmnet_log(rmnet_mhi_ptr, - MSG_ERROR, + rmnet_log(rmnet_mhi_ptr, MSG_ERROR, "Cannot set LPM value, MHI is not up.\n"); + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); return -ENODEV; } + read_unlock_bh(&rmnet_mhi_ptr->pm_lock); break; default: rc = -EINVAL; @@ -832,9 +849,8 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) "Failed to start TX chan ret %d\n", r); goto mhi_tx_chan_start_fail; - } else { - rmnet_mhi_ptr->tx_enabled = 1; } + client_handle = rmnet_mhi_ptr->tx_client_handle; } if (rmnet_mhi_ptr->rx_client_handle != NULL) { @@ -848,8 +864,6 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) "Failed to start RX chan ret %d\n", r); goto mhi_rx_chan_start_fail; - } else { - rmnet_mhi_ptr->rx_enabled = 1; } /* Both tx & rx client handle contain same device info */ client_handle = rmnet_mhi_ptr->rx_client_handle; @@ -860,62 +874,64 @@ static int rmnet_mhi_enable_iface(struct rmnet_mhi_private *rmnet_mhi_ptr) goto net_dev_alloc_fail; } - snprintf(ifalias, - sizeof(ifalias), - "%s_%04x_%02u.%02u.%02u_%u", - rmnet_mhi_ptr->interface_name, - client_handle->dev_id, - client_handle->domain, - client_handle->bus, - client_handle->slot, - rmnet_mhi_ptr->dev_id); - - snprintf(ifname, sizeof(ifname), "%s%%d", - rmnet_mhi_ptr->interface_name); - rtnl_lock(); - rmnet_mhi_ptr->dev = - alloc_netdev(sizeof(struct rmnet_mhi_private *), - ifname, NET_NAME_PREDICTABLE, rmnet_mhi_setup); if (!rmnet_mhi_ptr->dev) { - rmnet_log(rmnet_mhi_ptr, - MSG_CRITICAL, - "Network device allocation failed\n"); - ret = -ENOMEM; - goto net_dev_alloc_fail; + snprintf(ifalias, sizeof(ifalias), + "%s_%04x_%02u.%02u.%02u_%u", + rmnet_mhi_ptr->interface_name, + client_handle->dev_id, + client_handle->domain, + client_handle->bus, + client_handle->slot, + rmnet_mhi_ptr->dev_id); + + snprintf(ifname, sizeof(ifname), "%s%%d", + rmnet_mhi_ptr->interface_name); + + rtnl_lock(); + rmnet_mhi_ptr->dev = alloc_netdev( + sizeof(struct rmnet_mhi_private *), + ifname, NET_NAME_PREDICTABLE, rmnet_mhi_setup); + + if (!rmnet_mhi_ptr->dev) { + rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, + "Network device allocation failed\n"); + ret = -ENOMEM; + goto net_dev_alloc_fail; + } + SET_NETDEV_DEV(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->pdev->dev); + dev_set_alias(rmnet_mhi_ptr->dev, ifalias, strlen(ifalias)); + rmnet_mhi_ctxt = netdev_priv(rmnet_mhi_ptr->dev); + rtnl_unlock(); + *rmnet_mhi_ctxt = rmnet_mhi_ptr; + + ret = dma_set_mask(&rmnet_mhi_ptr->dev->dev, MHI_DMA_MASK); + if (ret) + rmnet_mhi_ptr->allocation_flags = GFP_KERNEL; + else + rmnet_mhi_ptr->allocation_flags = GFP_DMA; + + netif_napi_add(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->napi, + rmnet_mhi_poll, MHI_NAPI_WEIGHT_VALUE); + + ret = register_netdev(rmnet_mhi_ptr->dev); + if (ret) { + rmnet_log(rmnet_mhi_ptr, MSG_CRITICAL, + "Network device registration failed\n"); + goto net_dev_reg_fail; + } } - SET_NETDEV_DEV(rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->pdev->dev); - dev_set_alias(rmnet_mhi_ptr->dev, ifalias, strlen(ifalias)); - rmnet_mhi_ctxt = netdev_priv(rmnet_mhi_ptr->dev); - rtnl_unlock(); - *rmnet_mhi_ctxt = rmnet_mhi_ptr; - - ret = dma_set_mask(&(rmnet_mhi_ptr->dev->dev), - MHI_DMA_MASK); - if (ret) - rmnet_mhi_ptr->allocation_flags = GFP_KERNEL; - else - rmnet_mhi_ptr->allocation_flags = GFP_DMA; + + write_lock_irq(&rmnet_mhi_ptr->pm_lock); + rmnet_mhi_ptr->mhi_enabled = 1; + write_unlock_irq(&rmnet_mhi_ptr->pm_lock); r = rmnet_mhi_init_inbound(rmnet_mhi_ptr); if (r) { - rmnet_log(rmnet_mhi_ptr, - MSG_CRITICAL, - "Failed to init inbound ret %d\n", - r); + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "Failed to init inbound ret %d\n", r); } - netif_napi_add(rmnet_mhi_ptr->dev, &(rmnet_mhi_ptr->napi), - rmnet_mhi_poll, MHI_NAPI_WEIGHT_VALUE); - - rmnet_mhi_ptr->mhi_enabled = 1; - ret = register_netdev(rmnet_mhi_ptr->dev); - if (ret) { - rmnet_log(rmnet_mhi_ptr, - MSG_CRITICAL, - "Network device registration failed\n"); - goto net_dev_reg_fail; - } napi_enable(&(rmnet_mhi_ptr->napi)); rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Exited.\n"); @@ -951,25 +967,47 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info) switch (cb_info->cb_reason) { case MHI_CB_MHI_DISABLED: - rmnet_log(rmnet_mhi_ptr, - MSG_CRITICAL, - "Got MHI_DISABLED notification. Stopping stack\n"); - if (rmnet_mhi_ptr->mhi_enabled) { - rmnet_mhi_ptr->mhi_enabled = 0; - /* Ensure MHI is disabled before other mem ops */ - wmb(); - while (atomic_read(&rmnet_mhi_ptr->pending_data)) { - rmnet_log(rmnet_mhi_ptr, - MSG_CRITICAL, - "Waiting for channels to stop.\n"); - msleep(25); - } + case MHI_CB_MHI_SHUTDOWN: + case MHI_CB_SYS_ERROR: + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "Got MHI_SYS_ERROR notification. Stopping stack\n"); + + /* Disable interface on first notification. Long + * as we set mhi_enabled = 0, we gurantee rest of + * driver will not touch any critical data. + */ + write_lock_irq(&rmnet_mhi_ptr->pm_lock); + rmnet_mhi_ptr->mhi_enabled = 0; + write_unlock_irq(&rmnet_mhi_ptr->pm_lock); + + if (cb_info->chan == rmnet_mhi_ptr->rx_channel) { + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "Receive MHI_DISABLE notification for rx path\n"); rmnet_mhi_disable(rmnet_mhi_ptr); + } else { + rmnet_log(rmnet_mhi_ptr, MSG_INFO, + "Receive MHI_DISABLE notification for tx path\n"); + rmnet_mhi_ptr->tx_enabled = 0; + rmnet_mhi_internal_clean_unmap_buffers + (rmnet_mhi_ptr->dev, &rmnet_mhi_ptr->tx_buffers, + DMA_TO_DEVICE); + } + + /* Remove all votes disabling low power mode */ + if (!rmnet_mhi_ptr->tx_enabled && !rmnet_mhi_ptr->rx_enabled) { + struct mhi_client_handle *handle = + rmnet_mhi_ptr->rx_client_handle; + + if (!handle) + handle = rmnet_mhi_ptr->tx_client_handle; + while (rmnet_mhi_ptr->wake_count) { + mhi_set_lpm(handle, true); + rmnet_mhi_ptr->wake_count--; + } } break; case MHI_CB_MHI_ENABLED: - rmnet_log(rmnet_mhi_ptr, - MSG_CRITICAL, + rmnet_log(rmnet_mhi_ptr, MSG_INFO, "Got MHI_ENABLED notification. Starting stack\n"); if (cb_info->chan == rmnet_mhi_ptr->rx_channel) rmnet_mhi_ptr->rx_enabled = 1; @@ -998,16 +1036,10 @@ static void rmnet_mhi_cb(struct mhi_cb_info *cb_info) } break; case MHI_CB_XFER: - atomic_inc(&rmnet_mhi_ptr->pending_data); - /* Flush pending data is set before any other mem operations */ - wmb(); - if (rmnet_mhi_ptr->mhi_enabled) { - if (cb_info->chan == rmnet_mhi_ptr->rx_channel) - rmnet_mhi_rx_cb(cb_info->result); - else - rmnet_mhi_tx_cb(cb_info->result); - } - atomic_dec(&rmnet_mhi_ptr->pending_data); + if (cb_info->chan == rmnet_mhi_ptr->rx_channel) + rmnet_mhi_rx_cb(cb_info->result); + else + rmnet_mhi_tx_cb(cb_info->result); break; default: break; @@ -1172,6 +1204,7 @@ static int rmnet_mhi_probe(struct platform_device *pdev) return -ENOMEM; rmnet_mhi_ptr->pdev = pdev; spin_lock_init(&rmnet_mhi_ptr->out_chan_full_lock); + rwlock_init(&rmnet_mhi_ptr->pm_lock); rc = of_property_read_u32(pdev->dev.of_node, "qcom,mhi-mru", diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index 553480660722..100bbd582a5e 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -3848,11 +3848,8 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p, } ipa_ctx->logbuf = ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa", 0); - if (ipa_ctx->logbuf == NULL) { - IPAERR("failed to get logbuf\n"); - result = -ENOMEM; - goto fail_logbuf; - } + if (ipa_ctx->logbuf == NULL) + IPAERR("failed to create IPC log, continue...\n"); ipa_ctx->pdev = ipa_dev; ipa_ctx->uc_pdev = ipa_dev; @@ -4390,8 +4387,8 @@ fail_bus_reg: fail_bind: kfree(ipa_ctx->ctrl); fail_mem_ctrl: - ipc_log_context_destroy(ipa_ctx->logbuf); -fail_logbuf: + if (ipa_ctx->logbuf) + ipc_log_context_destroy(ipa_ctx->logbuf); kfree(ipa_ctx); ipa_ctx = NULL; fail_mem_ctx: diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index 5b706b6f493b..ddff50834f03 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -4264,11 +4264,8 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, } ipa3_ctx->logbuf = ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa", 0); - if (ipa3_ctx->logbuf == NULL) { - IPAERR("failed to get logbuf\n"); - result = -ENOMEM; - goto fail_logbuf; - } + if (ipa3_ctx->logbuf == NULL) + IPAERR("failed to create IPC log, continue...\n"); ipa3_ctx->pdev = ipa_dev; ipa3_ctx->uc_pdev = ipa_dev; @@ -4769,8 +4766,8 @@ fail_bind: fail_mem_ctrl: kfree(ipa3_ctx->ipa_tz_unlock_reg); fail_tz_unlock_reg: - ipc_log_context_destroy(ipa3_ctx->logbuf); -fail_logbuf: + if (ipa3_ctx->logbuf) + ipc_log_context_destroy(ipa3_ctx->logbuf); kfree(ipa3_ctx); ipa3_ctx = NULL; fail_mem_ctx: diff --git a/drivers/platform/msm/mhi_uci/mhi_uci.c b/drivers/platform/msm/mhi_uci/mhi_uci.c index ab3c3503c2fc..3191ec065a95 100644 --- a/drivers/platform/msm/mhi_uci/mhi_uci.c +++ b/drivers/platform/msm/mhi_uci/mhi_uci.c @@ -1185,9 +1185,43 @@ static void uci_xfer_cb(struct mhi_cb_info *cb_info) mutex_unlock(&chan_attr->chan_lock); wake_up(&chan_attr->wq); break; + case MHI_CB_SYS_ERROR: + case MHI_CB_MHI_SHUTDOWN: case MHI_CB_MHI_DISABLED: uci_log(uci_handle->uci_ipc_log, UCI_DBG_INFO, - "MHI disabled CB received\n"); + "MHI disabled CB received 0x%x for chan:%d\n", + cb_info->cb_reason, cb_info->chan); + + chan_attr = (cb_info->chan % 2) ? &uci_handle->in_attr : + &uci_handle->out_attr; + mutex_lock(&chan_attr->chan_lock); + chan_attr->enabled = false; + /* we disable entire handler by grabbing only one lock */ + uci_handle->enabled = false; + mutex_unlock(&chan_attr->chan_lock); + wake_up(&chan_attr->wq); + + /* + * if it's ctrl channel clear the resource now + * otherwise during file close we will release the + * resources + */ + if (uci_handle == uci_handle->uci_ctxt->ctrl_client && + chan_attr == &uci_handle->out_attr) { + struct uci_buf *itr, *tmp; + + mutex_lock(&chan_attr->chan_lock); + atomic_set(&uci_handle->out_attr.avail_pkts, 0); + atomic_set(&uci_handle->out_pkt_pend_ack, 0); + list_for_each_entry_safe(itr, tmp, &chan_attr->buf_head, + node) { + list_del(&itr->node); + kfree(itr->data); + } + atomic_set(&uci_handle->completion_ack, 0); + INIT_LIST_HEAD(&uci_handle->out_attr.buf_head); + mutex_unlock(&chan_attr->chan_lock); + } break; case MHI_CB_XFER: if (!cb_info->result) { diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c index 438da2c51dd6..723b9eaf658a 100644 --- a/drivers/power/power_supply_sysfs.c +++ b/drivers/power/power_supply_sysfs.c @@ -293,6 +293,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(die_health), POWER_SUPPLY_ATTR(connector_health), POWER_SUPPLY_ATTR(ctm_current_max), + POWER_SUPPLY_ATTR(hw_current_max), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index 0c80c8be7909..914a6e4eae64 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) "QCOM-BATT: %s: " fmt, __func__ #include <linux/device.h> +#include <linux/delay.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> @@ -36,6 +37,7 @@ #define PL_HW_ABSENT_VOTER "PL_HW_ABSENT_VOTER" #define PL_VOTER "PL_VOTER" #define RESTRICT_CHG_VOTER "RESTRICT_CHG_VOTER" +#define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER" struct pl_data { int pl_mode; @@ -49,14 +51,16 @@ struct pl_data { struct votable *pl_disable_votable; struct votable *pl_awake_votable; struct votable *hvdcp_hw_inov_dis_votable; + struct votable *usb_icl_votable; struct work_struct status_change_work; struct work_struct pl_disable_forever_work; struct delayed_work pl_taper_work; struct power_supply *main_psy; struct power_supply *pl_psy; struct power_supply *batt_psy; + struct power_supply *usb_psy; int charge_type; - int main_settled_ua; + int total_settled_ua; int pl_settled_ua; struct class qcom_batt_class; struct wakeup_source *pl_ws; @@ -92,15 +96,10 @@ enum { ********/ static void split_settled(struct pl_data *chip) { - int slave_icl_pct; + int slave_icl_pct, total_current_ua; int slave_ua = 0, main_settled_ua = 0; union power_supply_propval pval = {0, }; - int rc; - - /* TODO some parallel chargers do not have a fine ICL resolution. For - * them implement a psy interface which returns the closest lower ICL - * for desired split - */ + int rc, total_settled_ua = 0; if ((chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN) && (chip->pl_mode != POWER_SUPPLY_PL_USBIN_USBIN_EXT)) @@ -122,12 +121,31 @@ static void split_settled(struct pl_data *chip) slave_icl_pct = max(0, chip->slave_pct - 10); slave_ua = ((main_settled_ua + chip->pl_settled_ua) * slave_icl_pct) / 100; + total_settled_ua = main_settled_ua + chip->pl_settled_ua; } - /* ICL_REDUCTION on main could be 0mA when pl is disabled */ - pval.intval = slave_ua; + total_current_ua = get_effective_result_locked(chip->usb_icl_votable); + if (total_current_ua < 0) { + if (!chip->usb_psy) + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + pr_err("Couldn't get usbpsy while splitting settled\n"); + return; + } + /* no client is voting, so get the total current from charger */ + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &pval); + if (rc < 0) { + pr_err("Couldn't get max current rc=%d\n", rc); + return; + } + total_current_ua = pval.intval; + } + + pval.intval = total_current_ua - slave_ua; + /* Set ICL on main charger */ rc = power_supply_set_property(chip->main_psy, - POWER_SUPPLY_PROP_ICL_REDUCTION, &pval); + POWER_SUPPLY_PROP_CURRENT_MAX, &pval); if (rc < 0) { pr_err("Couldn't change slave suspend state rc=%d\n", rc); return; @@ -142,10 +160,12 @@ static void split_settled(struct pl_data *chip) return; } - /* main_settled_ua represents the total capability of adapter */ - if (!chip->main_settled_ua) - chip->main_settled_ua = main_settled_ua; + chip->total_settled_ua = total_settled_ua; chip->pl_settled_ua = slave_ua; + + pl_dbg(chip, PR_PARALLEL, + "Split total_current_ua=%d main_settled_ua=%d slave_ua=%d\n", + total_current_ua, main_settled_ua, slave_ua); } static ssize_t version_show(struct class *c, struct class_attribute *attr, @@ -213,6 +233,10 @@ static ssize_t restrict_chg_store(struct class *c, struct class_attribute *attr, chip->restricted_charging_enabled = !!val; + /* disable parallel charger in case of restricted charging */ + vote(chip->pl_disable_votable, RESTRICT_CHG_VOTER, + chip->restricted_charging_enabled, 0); + vote(chip->fcc_votable, RESTRICT_CHG_VOTER, chip->restricted_charging_enabled, chip->restricted_current); @@ -487,6 +511,59 @@ static int pl_fv_vote_callback(struct votable *votable, void *data, return 0; } +#define ICL_STEP_UV 25000 +static int usb_icl_vote_callback(struct votable *votable, void *data, + int icl_ua, const char *client) +{ + int rc; + struct pl_data *chip = data; + union power_supply_propval pval = {0, }; + + if (!chip->main_psy) + return 0; + + if (client == NULL) + icl_ua = INT_MAX; + + /* + * Disable parallel for new ICL vote - the call to split_settled will + * ensure that all the input current limit gets assigned to the main + * charger. + */ + vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, true, 0); + + /* rerun AICL */ + /* get the settled current */ + rc = power_supply_get_property(chip->main_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, + &pval); + if (rc < 0) { + pr_err("Couldn't get aicl settled value rc=%d\n", rc); + return rc; + } + + /* rerun AICL if new ICL is above settled ICL */ + if (icl_ua > pval.intval) { + /* set a lower ICL */ + pval.intval = max(pval.intval - ICL_STEP_UV, ICL_STEP_UV); + power_supply_set_property(chip->main_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, + &pval); + /* wait for ICL change */ + msleep(100); + + pval.intval = icl_ua; + power_supply_set_property(chip->main_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, + &pval); + /* wait for ICL change */ + msleep(100); + } + vote(chip->pl_disable_votable, ICL_CHANGE_VOTER, false, 0); + + return 0; +} + static void pl_disable_forever_work(struct work_struct *work) { struct pl_data *chip = container_of(work, @@ -508,7 +585,7 @@ static int pl_disable_vote_callback(struct votable *votable, int rc; chip->taper_pct = 100; - chip->main_settled_ua = 0; + chip->total_settled_ua = 0; chip->pl_settled_ua = 0; if (!pl_disable) { /* enable */ @@ -596,13 +673,15 @@ static int pl_awake_vote_callback(struct votable *votable, static bool is_main_available(struct pl_data *chip) { - if (!chip->main_psy) - chip->main_psy = power_supply_get_by_name("main"); + if (chip->main_psy) + return true; - if (!chip->main_psy) - return false; + chip->main_psy = power_supply_get_by_name("main"); - return true; + if (chip->main_psy) + rerun_election(chip->usb_icl_votable); + + return !!chip->main_psy; } static bool is_batt_available(struct pl_data *chip) @@ -711,6 +790,7 @@ static void handle_main_charge_type(struct pl_data *chip) static void handle_settled_icl_change(struct pl_data *chip) { union power_supply_propval pval = {0, }; + int new_total_settled_ua; int rc; if (get_effective_result(chip->pl_disable_votable)) @@ -730,9 +810,15 @@ static void handle_settled_icl_change(struct pl_data *chip) return; } + new_total_settled_ua = pval.intval + chip->pl_settled_ua; + pl_dbg(chip, PR_PARALLEL, + "total_settled_ua=%d settled_ua=%d new_total_settled_ua=%d\n", + chip->total_settled_ua, pval.intval, + new_total_settled_ua); + /* If ICL change is small skip splitting */ - if (abs((chip->main_settled_ua - chip->pl_settled_ua) - - pval.intval) > MIN_ICL_CHANGE_DELTA_UA) + if (abs(new_total_settled_ua - chip->total_settled_ua) + > MIN_ICL_CHANGE_DELTA_UA) split_settled(chip); } else { rerun_election(chip->fcc_votable); @@ -855,6 +941,14 @@ static int pl_init(void) goto destroy_votable; } + chip->usb_icl_votable = create_votable("USB_ICL", VOTE_MIN, + usb_icl_vote_callback, + chip); + if (IS_ERR(chip->usb_icl_votable)) { + rc = PTR_ERR(chip->usb_icl_votable); + goto destroy_votable; + } + chip->pl_disable_votable = create_votable("PL_DISABLE", VOTE_SET_ANY, pl_disable_vote_callback, chip); @@ -909,6 +1003,7 @@ destroy_votable: destroy_votable(chip->pl_disable_votable); destroy_votable(chip->fv_votable); destroy_votable(chip->fcc_votable); + destroy_votable(chip->usb_icl_votable); release_wakeup_source: wakeup_source_unregister(chip->pl_ws); cleanup: diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index b4fb80a2d4f3..4dfa43012b54 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -414,6 +414,7 @@ static enum power_supply_property smb2_usb_props[] = { POWER_SUPPLY_PROP_BOOST_CURRENT, POWER_SUPPLY_PROP_PE_START, POWER_SUPPLY_PROP_CTM_CURRENT_MAX, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, }; static int smb2_usb_get_prop(struct power_supply *psy, @@ -502,6 +503,9 @@ static int smb2_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CTM_CURRENT_MAX: val->intval = get_client_vote(chg->usb_icl_votable, CTM_VOTER); break; + case POWER_SUPPLY_PROP_HW_CURRENT_MAX: + rc = smblib_get_charge_current(chg, &val->intval); + break; default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; @@ -610,12 +614,12 @@ static int smb2_init_usb_psy(struct smb2 *chip) static enum power_supply_property smb2_usb_main_props[] = { POWER_SUPPLY_PROP_VOLTAGE_MAX, - POWER_SUPPLY_PROP_ICL_REDUCTION, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_TYPE, POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, POWER_SUPPLY_PROP_FCC_DELTA, + POWER_SUPPLY_PROP_CURRENT_MAX, /* * TODO move the TEMP and TEMP_MAX properties here, * and update the thermal balancer to look here @@ -634,9 +638,6 @@ static int smb2_usb_main_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_get_charge_param(chg, &chg->param.fv, &val->intval); break; - case POWER_SUPPLY_PROP_ICL_REDUCTION: - val->intval = chg->icl_reduction_ua; - break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: rc = smblib_get_charge_param(chg, &chg->param.fcc, &val->intval); @@ -653,6 +654,9 @@ static int smb2_usb_main_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_FCC_DELTA: rc = smblib_get_prop_fcc_delta(chg, val); break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = get_effective_result(chg->usb_icl_votable); + break; default: pr_debug("get prop %d is not supported in usb-main\n", psp); rc = -EINVAL; @@ -677,12 +681,12 @@ static int smb2_usb_main_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_set_charge_param(chg, &chg->param.fv, val->intval); break; - case POWER_SUPPLY_PROP_ICL_REDUCTION: - rc = smblib_set_icl_reduction(chg, val->intval); - break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: rc = smblib_set_charge_param(chg, &chg->param.fcc, val->intval); break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = smblib_set_icl_current(chg, val->intval); + break; default: pr_err("set prop %d is not supported\n", psp); rc = -EINVAL; @@ -1572,6 +1576,16 @@ static int smb2_init_hw(struct smb2 *chip) return rc; } + /* disable h/w autonomous parallel charging control */ + rc = smblib_masked_write(chg, MISC_CFG_REG, + STAT_PARALLEL_1400MA_EN_CFG_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't disable h/w autonomous parallel control rc=%d\n", + rc); + return rc; + } + /* configure float charger options */ switch (chip->dt.float_option) { case 1: @@ -2230,6 +2244,8 @@ static int smb2_probe(struct platform_device *pdev) } batt_charge_type = val.intval; + device_init_wakeup(chg->dev, true); + pr_info("QPNP SMB2 probed successfully usb:present=%d type=%d batt:present = %d health = %d charge = %d\n", usb_present, chg->usb_psy_desc.type, batt_present, batt_health, batt_charge_type); diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index d631f49db63a..bd96703579f6 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -654,10 +654,13 @@ static void smblib_uusb_removal(struct smb_charger *chg) { int rc; + cancel_delayed_work_sync(&chg->pl_enable_work); + vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0); + vote(chg->awake_votable, PL_DELAY_VOTER, false, 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); - vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0); cancel_delayed_work_sync(&chg->hvdcp_detect_work); @@ -693,13 +696,6 @@ static void smblib_uusb_removal(struct smb_charger *chg) if (rc < 0) smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n", rc); - - /* clear USB ICL vote for PL_USBIN_USBIN_VOTER */ - rc = vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0); - if (rc < 0) - smblib_err(chg, - "Couldn't un-vote PL_USBIN_USBIN from USB ICL rc=%d\n", - rc); } void smblib_suspend_on_debug_battery(struct smb_charger *chg) @@ -795,29 +791,12 @@ static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count) return 0; } -/********************* - * VOTABLE CALLBACKS * - *********************/ - -static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, - int suspend, const char *client) -{ - struct smb_charger *chg = data; - - /* resume input if suspend is invalid */ - if (suspend < 0) - suspend = 0; - - return smblib_set_dc_suspend(chg, (bool)suspend); -} - #define USBIN_25MA 25000 #define USBIN_100MA 100000 #define USBIN_150MA 150000 #define USBIN_500MA 500000 #define USBIN_900MA 900000 - static int set_sdp_current(struct smb_charger *chg, int icl_ua) { int rc; @@ -856,20 +835,18 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) return rc; } -static int smblib_usb_icl_vote_callback(struct votable *votable, void *data, - int icl_ua, const char *client) +int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) { - struct smb_charger *chg = data; int rc = 0; bool override; union power_supply_propval pval; /* suspend and return if 25mA or less is requested */ - if (client && (icl_ua < USBIN_25MA)) + if (icl_ua < USBIN_25MA) return smblib_set_usb_suspend(chg, true); disable_irq_nosync(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq); - if (!client) + if (icl_ua == INT_MAX) goto override_suspend_config; rc = smblib_get_prop_typec_mode(chg, &pval); @@ -887,8 +864,7 @@ static int smblib_usb_icl_vote_callback(struct votable *votable, void *data, goto enable_icl_changed_interrupt; } } else { - rc = smblib_set_charge_param(chg, &chg->param.usb_icl, - icl_ua - chg->icl_reduction_ua); + 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); goto enable_icl_changed_interrupt; @@ -898,7 +874,7 @@ static int smblib_usb_icl_vote_callback(struct votable *votable, void *data, override_suspend_config: /* determine if override needs to be enforced */ override = true; - if (client == NULL) { + if (icl_ua == INT_MAX) { /* remove override if no voters - hw defaults is desired */ override = false; } else if (pval.intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { @@ -906,7 +882,7 @@ override_suspend_config: /* For std cable with type = SDP never override */ override = false; else if (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB_CDP - && icl_ua - chg->icl_reduction_ua == 1500000) + && icl_ua == 1500000) /* * For std cable with type = CDP override only if * current is not 1500mA @@ -936,6 +912,22 @@ enable_icl_changed_interrupt: return rc; } +/********************* + * VOTABLE CALLBACKS * + *********************/ + +static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, + int suspend, const char *client) +{ + struct smb_charger *chg = data; + + /* resume input if suspend is invalid */ + if (suspend < 0) + suspend = 0; + + return smblib_set_dc_suspend(chg, (bool)suspend); +} + static int smblib_dc_icl_vote_callback(struct votable *votable, void *data, int icl_ua, const char *client) { @@ -1793,6 +1785,10 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg, return -EINVAL; chg->system_temp_level = val->intval; + /* disable parallel charge in case of system temp level */ + vote(chg->pl_disable_votable, THERMAL_DAEMON_VOTER, + chg->system_temp_level ? true : false, 0); + if (chg->system_temp_level == chg->thermal_levels) return vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, true, 0); @@ -2027,7 +2023,7 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, int rc = 0; u8 stat; - if (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0) { + if (get_client_vote_locked(chg->usb_icl_votable, USER_VOTER) == 0) { val->intval = false; return rc; } @@ -2605,29 +2601,12 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, "Couldn't un-vote DCP from USB ICL rc=%d\n", rc); - /* clear USB ICL vote for PL_USBIN_USBIN_VOTER */ - rc = vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0); - if (rc < 0) - smblib_err(chg, - "Couldn't un-vote PL_USBIN_USBIN from USB ICL rc=%d\n", - rc); - /* remove USB_PSY_VOTER */ rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); if (rc < 0) { smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc); return rc; } - - /* pd active set, parallel charger can be enabled now */ - rc = vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, - false, 0); - if (rc < 0) { - smblib_err(chg, - "Couldn't unvote PL_DELAY_HVDCP_VOTER rc=%d\n", - rc); - return rc; - } } /* CC pin selection s/w override in PD session; h/w otherwise. */ @@ -2876,15 +2855,21 @@ int smblib_get_prop_fcc_delta(struct smb_charger *chg, #define TYPEC_DEFAULT_CURRENT_MA 900000 #define TYPEC_MEDIUM_CURRENT_MA 1500000 #define TYPEC_HIGH_CURRENT_MA 3000000 -static int smblib_get_charge_current(struct smb_charger *chg, +int smblib_get_charge_current(struct smb_charger *chg, int *total_current_ua) { const struct apsd_result *apsd_result = smblib_update_usb_type(chg); union power_supply_propval val = {0, }; - int rc, typec_source_rd, current_ua; + int rc = 0, typec_source_rd, current_ua; bool non_compliant; u8 stat5; + if (chg->pd_active) { + *total_current_ua = + get_client_vote_locked(chg->usb_icl_votable, PD_VOTER); + return rc; + } + rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat5); if (rc < 0) { smblib_err(chg, "Couldn't read TYPE_C_STATUS_5 rc=%d\n", rc); @@ -2959,39 +2944,12 @@ static int smblib_get_charge_current(struct smb_charger *chg, return 0; } -int smblib_set_icl_reduction(struct smb_charger *chg, int reduction_ua) -{ - int current_ua, rc; - - if (reduction_ua == 0) { - vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0); - } else { - /* - * No usb_icl voter means we are defaulting to hw chosen - * max limit. We need a vote from s/w to enforce the reduction. - */ - if (get_effective_result(chg->usb_icl_votable) == -EINVAL) { - rc = smblib_get_charge_current(chg, ¤t_ua); - if (rc < 0) { - pr_err("Failed to get ICL rc=%d\n", rc); - return rc; - } - vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, true, - current_ua); - } - } - - chg->icl_reduction_ua = reduction_ua; - - return rerun_election(chg->usb_icl_votable); -} - /************************ * PARALLEL PSY GETTERS * ************************/ int smblib_get_prop_slave_current_now(struct smb_charger *chg, - union power_supply_propval *pval) + union power_supply_propval *pval) { if (IS_ERR_OR_NULL(chg->iio.batt_i_chan)) chg->iio.batt_i_chan = iio_channel_get(chg->dev, "batt_i"); @@ -3054,7 +3012,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) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", - rc); + rc); return IRQ_HANDLED; } @@ -3159,6 +3117,7 @@ irqreturn_t smblib_handle_usbin_uv(int irq, void *data) return IRQ_HANDLED; } +#define PL_DELAY_MS 30000 irqreturn_t smblib_handle_usb_plugin(int irq, void *data) { struct smb_irq_data *irq_data = data; @@ -3197,6 +3156,11 @@ irqreturn_t smblib_handle_usb_plugin(int irq, void *data) smblib_err(chg, "Couldn't enable dpdm regulator rc=%d\n", rc); } + + /* Schedule work to enable parallel charger */ + vote(chg->awake_votable, PL_DELAY_VOTER, true, 0); + schedule_delayed_work(&chg->pl_enable_work, + msecs_to_jiffies(PL_DELAY_MS)); } else { if (chg->wa_flags & BOOST_BACK_WA) vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0); @@ -3374,9 +3338,6 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, } } - /* QC authentication done, parallel charger can be enabled now */ - vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0); - smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n", apsd_result->name); } @@ -3406,15 +3367,6 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, /* enforce DCP ICL if specified */ vote(chg->usb_icl_votable, DCP_VOTER, chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua); - /* - * If adapter is not QC2.0/QC3.0 remove vote for parallel - * disable. - * Otherwise if adapter is QC2.0/QC3.0 wait for authentication - * to complete. - */ - if (!qc_charger) - vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, - false, 0); } smblib_dbg(chg, PR_INTERRUPT, "IRQ: smblib_handle_hvdcp_check_timeout %s\n", @@ -3486,13 +3438,9 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) true); case OCP_CHARGER_BIT: case FLOAT_CHARGER_BIT: - /* - * if not DCP then no hvdcp timeout happens. Enable - * pd/parallel here. - */ + /* if not DCP then no hvdcp timeout happens, Enable pd here. */ vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, false, 0); - vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0); break; case DCP_CHARGER_BIT: if (chg->wa_flags & QC_CHARGER_DETECTION_WA_BIT) @@ -3604,12 +3552,6 @@ static void typec_source_removal(struct smb_charger *chg) smblib_err(chg, "Couldn't un-vote DCP from USB ICL rc=%d\n", rc); - /* clear USB ICL vote for PL_USBIN_USBIN_VOTER */ - rc = vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0); - if (rc < 0) - smblib_err(chg, - "Couldn't un-vote PL_USBIN_USBIN from USB ICL rc=%d\n", - rc); } static void typec_source_insertion(struct smb_charger *chg) @@ -3645,9 +3587,12 @@ static void smblib_handle_typec_removal(struct smb_charger *chg) { int rc; + cancel_delayed_work_sync(&chg->pl_enable_work); + vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0); + vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); + vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0); - vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0); vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0); vote(chg->usb_irq_enable_votable, QC_VOTER, false, 0); @@ -3703,8 +3648,6 @@ static void smblib_handle_typec_insertion(struct smb_charger *chg, if (rp == POWER_SUPPLY_TYPEC_SOURCE_HIGH || rp == POWER_SUPPLY_TYPEC_NON_COMPLIANT) { smblib_dbg(chg, PR_MISC, "VBUS & CC could be shorted; keeping HVDCP disabled\n"); - /* HVDCP is not going to be enabled; enable parallel */ - vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, false, 0); vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0); } else { @@ -4210,6 +4153,16 @@ static void smblib_icl_change_work(struct work_struct *work) smblib_dbg(chg, PR_INTERRUPT, "icl_settled=%d\n", settled_ua); } +static void smblib_pl_enable_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + pl_enable_work.work); + + smblib_dbg(chg, PR_PARALLEL, "timer expired, enabling parallel\n"); + vote(chg->pl_disable_votable, PL_DELAY_VOTER, false, 0); + vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); +} + static int smblib_create_votables(struct smb_charger *chg) { int rc = 0; @@ -4226,13 +4179,19 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } + chg->usb_icl_votable = find_votable("USB_ICL"); + if (!chg->usb_icl_votable) { + rc = -EPROBE_DEFER; + return rc; + } + chg->pl_disable_votable = find_votable("PL_DISABLE"); if (!chg->pl_disable_votable) { rc = -EPROBE_DEFER; return rc; } vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0); - vote(chg->pl_disable_votable, PL_DELAY_HVDCP_VOTER, true, 0); + vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0); chg->dc_suspend_votable = create_votable("DC_SUSPEND", VOTE_SET_ANY, smblib_dc_suspend_vote_callback, @@ -4242,14 +4201,6 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } - chg->usb_icl_votable = create_votable("USB_ICL", VOTE_MIN, - smblib_usb_icl_vote_callback, - chg); - if (IS_ERR(chg->usb_icl_votable)) { - rc = PTR_ERR(chg->usb_icl_votable); - return rc; - } - chg->dc_icl_votable = create_votable("DC_ICL", VOTE_MIN, smblib_dc_icl_vote_callback, chg); @@ -4400,6 +4351,7 @@ int smblib_init(struct smb_charger *chg) INIT_WORK(&chg->vconn_oc_work, smblib_vconn_oc_work); INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); + INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); chg->fake_capacity = -EINVAL; switch (chg->mode) { diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 048e7c2b4091..49b9d3da783c 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -56,7 +56,7 @@ enum print_reason { #define MICRO_USB_VOTER "MICRO_USB_VOTER" #define DEBUG_BOARD_VOTER "DEBUG_BOARD_VOTER" #define PD_SUSPEND_SUPPORTED_VOTER "PD_SUSPEND_SUPPORTED_VOTER" -#define PL_DELAY_HVDCP_VOTER "PL_DELAY_HVDCP_VOTER" +#define PL_DELAY_VOTER "PL_DELAY_VOTER" #define CTM_VOTER "CTM_VOTER" #define SW_QC3_VOTER "SW_QC3_VOTER" #define AICL_RERUN_VOTER "AICL_RERUN_VOTER" @@ -288,6 +288,7 @@ struct smb_charger { struct work_struct vconn_oc_work; struct delayed_work otg_ss_done_work; struct delayed_work icl_change_work; + struct delayed_work pl_enable_work; /* cached status */ int voltage_min_uv; @@ -321,8 +322,6 @@ struct smb_charger { /* extcon for VBUS / ID notification to USB for uUSB */ struct extcon_dev *extcon; - int icl_reduction_ua; - /* qnovo */ int qnovo_fcc_ua; int qnovo_fv_uv; @@ -488,9 +487,10 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg); int smblib_get_prop_fcc_delta(struct smb_charger *chg, union power_supply_propval *val); int smblib_icl_override(struct smb_charger *chg, bool override); -int smblib_set_icl_reduction(struct smb_charger *chg, int reduction_ua); int smblib_dp_dm(struct smb_charger *chg, int val); int smblib_rerun_aicl(struct smb_charger *chg); +int smblib_set_icl_current(struct smb_charger *chg, int icl_ua); +int smblib_get_charge_current(struct smb_charger *chg, int *total_current_ua); int smblib_init(struct smb_charger *chg); int smblib_deinit(struct smb_charger *chg); diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h index b79060094cf6..167666a8c548 100644 --- a/drivers/power/supply/qcom/smb-reg.h +++ b/drivers/power/supply/qcom/smb-reg.h @@ -919,6 +919,7 @@ enum { #define MISC_CFG_REG (MISC_BASE + 0x52) #define GSM_PA_ON_ADJ_SEL_BIT BIT(0) +#define STAT_PARALLEL_1400MA_EN_CFG_BIT BIT(3) #define TCC_DEBOUNCE_20MS_BIT BIT(5) #define SNARL_BARK_BITE_WD_CFG_REG (MISC_BASE + 0x53) diff --git a/drivers/power/supply/qcom/smb1351-charger.c b/drivers/power/supply/qcom/smb1351-charger.c index 8467d167512f..7014fd706d9e 100644 --- a/drivers/power/supply/qcom/smb1351-charger.c +++ b/drivers/power/supply/qcom/smb1351-charger.c @@ -1655,7 +1655,7 @@ static int smb1351_parallel_get_property(struct power_supply *psy, switch (prop) { case POWER_SUPPLY_PROP_CHARGING_ENABLED: - val->intval = !chip->usb_suspended_status; + val->intval = !chip->parallel_charger_suspended; break; case POWER_SUPPLY_PROP_CURRENT_MAX: if (!chip->parallel_charger_suspended) diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c index 4916c87aced8..694591c3ec56 100644 --- a/drivers/power/supply/qcom/smb138x-charger.c +++ b/drivers/power/supply/qcom/smb138x-charger.c @@ -577,13 +577,15 @@ static int smb138x_parallel_get_prop(struct power_supply *psy, rc = smblib_get_usb_suspend(chg, &val->intval); break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: - if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) rc = smblib_get_prop_input_current_limited(chg, val); else val->intval = 0; break; case POWER_SUPPLY_PROP_CURRENT_MAX: - if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) rc = smblib_get_charge_param(chg, &chg->param.usb_icl, &val->intval); else @@ -669,7 +671,8 @@ static int smb138x_parallel_set_prop(struct power_supply *psy, rc = smb138x_set_parallel_suspend(chip, (bool)val->intval); break; case POWER_SUPPLY_PROP_CURRENT_MAX: - if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) rc = smblib_set_charge_param(chg, &chg->param.usb_icl, val->intval); break; @@ -1484,7 +1487,8 @@ static int smb138x_slave_probe(struct smb138x *chip) goto cleanup; } - if (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) { + if ((chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN) + || (chip->dt.pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT)) { rc = smb138x_init_vbus_regulator(chip); if (rc < 0) { pr_err("Couldn't initialize vbus regulator rc=%d\n", diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 3e858015813f..79bb3337ba36 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -4289,15 +4289,25 @@ static int __ufshcd_uic_hibern8_enter(struct ufs_hba *hba) * mode hence full reinit is required to move link to HS speeds. */ if (ret || hba->full_init_linereset) { + int err; + hba->full_init_linereset = false; ufshcd_update_error_stats(hba, UFS_ERR_HIBERN8_ENTER); dev_err(hba->dev, "%s: hibern8 enter failed. ret = %d", __func__, ret); /* - * If link recovery fails then return error so that caller - * don't retry the hibern8 enter again. + * If link recovery fails then return error code (-ENOLINK) + * returned ufshcd_link_recovery(). + * If link recovery succeeds then return -EAGAIN to attempt + * hibern8 enter retry again. */ - ret = ufshcd_link_recovery(hba); + err = ufshcd_link_recovery(hba); + if (err) { + dev_err(hba->dev, "%s: link recovery failed", __func__); + ret = err; + } else { + ret = -EAGAIN; + } } else { dev_dbg(hba->dev, "%s: Hibern8 Enter at %lld us", __func__, ktime_to_us(ktime_get())); @@ -4314,8 +4324,8 @@ int ufshcd_uic_hibern8_enter(struct ufs_hba *hba) ret = __ufshcd_uic_hibern8_enter(hba); if (!ret) goto out; - /* Unable to recover the link, so no point proceeding */ - if (ret == -ENOLINK) + else if (ret != -EAGAIN) + /* Unable to recover the link, so no point proceeding */ BUG(); } out: diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 0b35caa86d51..ab46eb70651c 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -2110,7 +2110,6 @@ static int icnss_driver_event_register_driver(void *data) power_off: icnss_hw_power_off(penv); - penv->ops = NULL; out: return ret; } @@ -2646,7 +2645,7 @@ int icnss_register_driver(struct icnss_driver_ops *ops) } ret = icnss_driver_event_post(ICNSS_DRIVER_EVENT_REGISTER_DRIVER, - ICNSS_EVENT_SYNC, ops); + 0, ops); if (ret == -EINTR) ret = 0; diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c index be532503954f..7216fdd4245d 100644 --- a/drivers/usb/gadget/function/f_gsi.c +++ b/drivers/usb/gadget/function/f_gsi.c @@ -1550,13 +1550,6 @@ static void gsi_ctrl_notify_resp_complete(struct usb_ep *ep, event->bNotificationType, req->status); /* FALLTHROUGH */ case 0: - /* - * handle multiple pending resp available - * notifications by queuing same until we're done, - * rest of the notification require queuing new - * request. - */ - gsi_ctrl_send_notification(gsi); break; } } @@ -1651,6 +1644,14 @@ static void gsi_ctrl_reset_cmd_complete(struct usb_ep *ep, gsi_ctrl_send_cpkt_tomodem(gsi, req->buf, 0); } +static void gsi_ctrl_send_response_complete(struct usb_ep *ep, + struct usb_request *req) +{ + struct f_gsi *gsi = req->context; + + gsi_ctrl_send_notification(gsi); +} + static int gsi_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) { @@ -1737,6 +1738,8 @@ gsi_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) memcpy(req->buf, cpkt->buf, value); gsi_ctrl_pkt_free(cpkt); + req->complete = gsi_ctrl_send_response_complete; + req->context = gsi; log_event_dbg("copied encap_resp %d bytes", value); break; diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 2d499ef903d3..226198efbeec 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1512,8 +1512,14 @@ exit_loop: pr_debug("end\n"); - /* Send a connect notification */ - if (!mdss_dp_is_phy_test_pattern_requested(dp_drv)) + /* + * Send a connect notification to clients except when processing link + * training and electrical compliance tests. There is no need to send + * a notification in these testing use cases as there is no + * expectation of receiving a video signal as part of the test. + */ + if (!mdss_dp_is_phy_test_pattern_requested(dp_drv) && + !mdss_dp_is_link_training_requested(dp_drv)) mdss_dp_notify_clients(dp_drv, NOTIFY_CONNECT_IRQ_HPD); return ret; @@ -1609,6 +1615,14 @@ int mdss_dp_on(struct mdss_panel_data *pdata) panel_data); if (dp_drv->power_on) { + /* + * Acknowledge the connection event if link training has already + * been done. This will unblock the external display thread and + * allow the driver to progress. For example, in the case of + * video test pattern requests, to send the test response and + * start transmitting the test pattern. + */ + mdss_dp_ack_state(dp_drv, true); pr_debug("Link already setup, return\n"); return 0; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 3d426520bfa1..31cc6f40baa5 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -434,6 +434,8 @@ struct mmc_card { enum mmc_pon_type pon_type; bool cmdq_init; struct mmc_bkops_info bkops; + bool err_in_sdr104; + bool sdr104_blocked; }; /* diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 1068953943d8..2a1a6fec179f 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -169,6 +169,7 @@ extern int __mmc_switch_cmdq_mode(struct mmc_command *cmd, u8 set, u8 index, extern int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); extern int mmc_set_auto_bkops(struct mmc_card *card, bool enable); +extern int mmc_suspend_clk_scaling(struct mmc_host *host); #define MMC_ERASE_ARG 0x00000000 #define MMC_SECURE_ERASE_ARG 0x80000000 diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 055b879dfa6b..d9e12c1b1748 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -595,6 +595,7 @@ struct mmc_host { struct io_latency_state io_lat_s; #endif + bool sdr104_wa; unsigned long private[0] ____cacheline_aligned; }; @@ -728,6 +729,16 @@ static inline int mmc_host_uhs(struct mmc_host *host) MMC_CAP_UHS_DDR50); } +static inline void mmc_host_clear_sdr104(struct mmc_host *host) +{ + host->caps &= ~MMC_CAP_UHS_SDR104; +} + +static inline void mmc_host_set_sdr104(struct mmc_host *host) +{ + host->caps |= MMC_CAP_UHS_SDR104; +} + static inline int mmc_host_packed_wr(struct mmc_host *host) { return host->caps2 & MMC_CAP2_PACKED_WR; diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 864f7f6a0d01..7488bb993d7a 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -246,6 +246,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_DIE_HEALTH, POWER_SUPPLY_PROP_CONNECTOR_HEALTH, POWER_SUPPLY_PROP_CTM_CURRENT_MAX, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index cc8e45d77fcd..e125ed8c2a16 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -9076,8 +9076,9 @@ static int tavil_device_down(struct wcd9xxx *wcd9xxx) codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); priv = snd_soc_codec_get_drvdata(codec); - swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev, - SWR_DEVICE_DOWN, NULL); + if (priv->swr.ctrl_data) + swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_DOWN, NULL); tavil_dsd_reset(priv->dsd_config); snd_soc_card_change_online_state(codec->component.card, 0); for (count = 0; count < NUM_CODEC_DAIS; count++) |