Merge "msm: ipa: fix race condition around sys->len"
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
index 6df71af..05fa6e4 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
@@ -178,6 +178,10 @@
Definition: WD bark-timeout in seconds. The possible values are
16, 32, 64, 128. If not defined it defaults to 64.
+- qcom,sw-jeita-enable
+ Usage: optional
+ Value type: bool
+ Definition: Boolean flag which when present enables sw compensation for jeita
=============================================
Second Level Nodes - SMB2 Charger Peripherals
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio.dtsi
new file mode 100644
index 0000000..f861ca3
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio.dtsi
@@ -0,0 +1,81 @@
+/* Copyright (c) 2017, 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 "sdm670-audio.dtsi"
+
+&msm_audio_ion {
+ iommus = <&apps_smmu 0x1821 0x0>;
+ qcom,smmu-sid-mask = /bits/ 64 <0xf>;
+};
+
+&qupv3_se8_spi {
+ status = "okay";
+};
+
+&pm660l_3 {
+ /delete-node/analog-codec;
+};
+
+&soc {
+ /delete-node/msm-sdw-codec@62ec1000;
+ /delete-node/sound;
+ /delete-node/cdc_pdm_pinctrl;
+ /delete-node/wsa_spkr_en1_pinctrl;
+ /delete-node/wsa_spkr_en2_pinctrl;
+ /delete-node/sdw_clk_data_pinctrl;
+};
+
+&msm_audio_ion {
+ iommus = <&apps_smmu 0x1821 0x0>;
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+ qcom,gpio-connect = <&tlmm 54 0>;
+};
+
+&wdsp_mgr {
+ status = "okay";
+};
+
+&wdsp_glink {
+ status = "okay";
+};
+
+&slim_aud {
+ status = "okay";
+};
+
+&dai_slim {
+ status = "okay";
+};
+
+&wcd934x_cdc {
+ status = "okay";
+};
+
+&clock_audio_lnbb {
+ status = "okay";
+};
+
+&wcd_rst_gpio {
+ status = "okay";
+};
+
+&wcd9xxx_intc {
+ status = "okay";
+};
+
+&tavil_snd {
+ status = "okay";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
index 853e28b..ad15615 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
@@ -12,3 +12,4 @@
#include "sdm845-cdp.dtsi"
#include "sdm845-interposer-pm660.dtsi"
+#include "sdm845-interposer-sdm670-audio.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
index 9320b22b..c709770 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
@@ -12,6 +12,7 @@
#include "sdm845-mtp.dtsi"
#include "sdm845-interposer-pm660.dtsi"
+#include "sdm845-interposer-sdm670-audio.dtsi"
&qupv3_se10_i2c {
/delete-node/ qcom,smb1355@8;
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index fe29336..d4ab8f4 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -515,12 +515,17 @@
#size-cells = <2>;
ranges;
- removed_region1: removed_region1@85700000 {
+ hyp_region: hyp_region@85700000 {
no-map;
- reg = <0 0x85700000 0 0x800000>;
+ reg = <0 0x85700000 0 0x600000>;
};
- removed_region2: removed_region2@85fc0000 {
+ xbl_region: xbl_region@85e00000 {
+ no-map;
+ reg = <0 0x85e00000 0 0x100000>;
+ };
+
+ removed_region: removed_region@85fc0000 {
no-map;
reg = <0 0x85fc0000 0 0x2f40000>;
};
@@ -531,64 +536,70 @@
reg = <0 0x8ab00000 0 0x500000>;
};
- pil_modem_mem: modem_region@8b000000 {
+ pil_ipa_fw_mem: pil_ipa_fw_region@8b000000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x8b000000 0 0x7300000>;
+ reg = <0 0x8b000000 0 0x10000>;
};
- pil_video_mem: pil_video_region@92300000 {
+ pil_ipa_gsi_mem: pil_ipa_gsi_region@8b010000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x92300000 0 0x500000>;
+ reg = <0 0x8b010000 0 0x5000>;
};
- pil_cdsp_mem: cdsp_regions@92800000 {
+ pil_gpu_mem: pil_gpu_region@8b015000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x92800000 0 0x800000>;
+ reg = <0 0x8b015000 0 0x1000>;
};
- pil_adsp_mem: pil_adsp_region@93000000 {
+ pil_adsp_mem: pil_adsp_region@8b100000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x93000000 0 0x1a00000>;
+ reg = <0 0x8b100000 0 0x1a00000>;
};
- pil_mba_mem: pil_mba_region@0x94a00000 {
- compatible = "removed-dma-pool";
- no-map;
- reg = <0 0x94a00000 0 0x200000>;
+ wlan_fw_region: wlan_fw_region@8cb00000 {
+ compatible = "shared-dma-pool";
+ reg = <0 0x8cb00000 0 0x100000>;
};
- pil_slpi_mem: pil_slpi_region@94c00000 {
+ pil_modem_mem: modem_region@8cc00000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x94c00000 0 0x1400000>;
+ reg = <0 0x8cc00000 0 0x7600000>;
};
- pil_ipa_fw_mem: pil_ipa_fw_region@96000000 {
+ pil_video_mem: pil_video_region@94200000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x96000000 0 0x10000>;
+ reg = <0 0x94200000 0 0x500000>;
};
- pil_ipa_gsi_mem: pil_ipa_gsi_region@96010000 {
+ pil_cdsp_mem: cdsp_regions@94700000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x96010000 0 0x5000>;
+ reg = <0 0x94700000 0 0x800000>;
};
- pil_gpu_mem: pil_gpu_region@96015000 {
+ pil_mba_mem: pil_mba_region@0x94f00000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x96015000 0 0x1000>;
+ reg = <0 0x94f00000 0 0x200000>;
};
- pil_spss_mem: spss_region@96100000 {
+ pil_slpi_mem: pil_slpi_region@95100000 {
compatible = "removed-dma-pool";
no-map;
- reg = <0 0x96100000 0 0x100000>;
+ reg = <0 0x95100000 0 0x1400000>;
+ };
+
+
+ pil_spss_mem: spss_region@96500000 {
+ compatible = "removed-dma-pool";
+ no-map;
+ reg = <0 0x96500000 0 0x100000>;
};
adsp_mem: adsp_region {
@@ -602,7 +613,7 @@
qseecom_mem: qseecom_region {
compatible = "shared-dma-pool";
alloc-ranges = <0 0x00000000 0 0xffffffff>;
- reusable;
+ no-map;
alignment = <0 0x400000>;
size = <0 0x1400000>;
};
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index a1c5710..0e8aef9 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -584,6 +584,7 @@
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_HARDENED_USERCOPY=y
+CONFIG_FORTIFY_SOURCE=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
CONFIG_CRYPTO_CTR=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 0940b48..c516cd3 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -654,6 +654,7 @@
CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
CONFIG_SECURITY=y
CONFIG_HARDENED_USERCOPY=y
+CONFIG_FORTIFY_SOURCE=y
CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SMACK=y
CONFIG_CRYPTO_CTR=y
diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c
index e4ee2a7..309648f 100644
--- a/drivers/bluetooth/btfm_slim_codec.c
+++ b/drivers/bluetooth/btfm_slim_codec.c
@@ -118,9 +118,6 @@
return;
}
- if (dai->id == BTFM_FM_SLIM_TX)
- goto out;
-
/* Search for dai->id matched port handler */
for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
(ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
@@ -134,7 +131,6 @@
}
btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan);
-out:
btfm_slim_hw_deinit(btfmslim);
}
@@ -205,61 +201,6 @@
return ret;
}
-static int btfm_slim_dai_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- int ret = -EINVAL, i;
- struct btfmslim *btfmslim = dai->dev->platform_data;
- struct btfmslim_ch *ch;
- uint8_t rxport, grp = false, nchan = 1;
-
- BTFMSLIM_DBG("dai->name: %s, dai->id: %d, dai->rate: %d", dai->name,
- dai->id, dai->rate);
-
- switch (dai->id) {
- case BTFM_FM_SLIM_TX:
- grp = true; nchan = 2;
- ch = btfmslim->tx_chs;
- rxport = 0;
- break;
- case BTFM_BT_SCO_SLIM_TX:
- ch = btfmslim->tx_chs;
- rxport = 0;
- break;
- case BTFM_BT_SCO_A2DP_SLIM_RX:
- case BTFM_BT_SPLIT_A2DP_SLIM_RX:
- ch = btfmslim->rx_chs;
- rxport = 1;
- break;
- case BTFM_SLIM_NUM_CODEC_DAIS:
- default:
- BTFMSLIM_ERR("dai->id is invalid:%d", dai->id);
- goto out;
- }
-
- if (dai->id != BTFM_FM_SLIM_TX) {
- ret = 0;
- goto out;
- }
-
- /* Search for dai->id matched port handler */
- for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
- (ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
- (ch->id != dai->id); ch++, i++)
- ;
-
- if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
- (ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
- BTFMSLIM_ERR("ch is invalid!!");
- goto out;
- }
-
- btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan);
-
-out:
- return ret;
-}
-
/* This function will be called once during boot up */
static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
@@ -405,7 +346,6 @@
.shutdown = btfm_slim_dai_shutdown,
.hw_params = btfm_slim_dai_hw_params,
.prepare = btfm_slim_dai_prepare,
- .hw_free = btfm_slim_dai_hw_free,
.set_channel_map = btfm_slim_dai_set_channel_map,
.get_channel_map = btfm_slim_dai_get_channel_map,
};
diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c
index 0c4e0b3..3d66fff 100644
--- a/drivers/bluetooth/btfm_slim_wcn3990.c
+++ b/drivers/bluetooth/btfm_slim_wcn3990.c
@@ -82,7 +82,7 @@
uint8_t rxport, uint8_t enable)
{
int ret = 0;
- uint8_t reg_val = 0;
+ uint8_t reg_val = 0, en;
uint8_t port_bit = 0;
uint16_t reg;
@@ -137,15 +137,15 @@
reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num);
enable_disable_rxport:
- if (enable) {
- if (is_fm_port(port_num))
- reg_val = CHRK_SB_PGD_PORT_ENABLE |
- CHRK_SB_PGD_PORT_WM_L3;
- else
- reg_val = CHRK_SB_PGD_PORT_ENABLE |
- CHRK_SB_PGD_PORT_WM_LB;
- } else
- reg_val = CHRK_SB_PGD_PORT_DISABLE;
+ if (enable)
+ en = CHRK_SB_PGD_PORT_ENABLE;
+ else
+ en = CHRK_SB_PGD_PORT_DISABLE;
+
+ if (is_fm_port(port_num))
+ reg_val = en | CHRK_SB_PGD_PORT_WM_L8;
+ else
+ reg_val = enable ? en | CHRK_SB_PGD_PORT_WM_LB : en;
ret = btfm_slim_write(btfmslim, reg, 1, ®_val, IFD);
if (ret)
diff --git a/drivers/bluetooth/btfm_slim_wcn3990.h b/drivers/bluetooth/btfm_slim_wcn3990.h
index 6bbdb6b..b2723ff 100644
--- a/drivers/bluetooth/btfm_slim_wcn3990.h
+++ b/drivers/bluetooth/btfm_slim_wcn3990.h
@@ -68,6 +68,7 @@
#define CHRK_SB_PGD_PORT_WM_L1 (0x1 << 1)
#define CHRK_SB_PGD_PORT_WM_L2 (0x2 << 1)
#define CHRK_SB_PGD_PORT_WM_L3 (0x3 << 1)
+#define CHRK_SB_PGD_PORT_WM_L8 (0x8 << 1)
#define CHRK_SB_PGD_PORT_WM_LB (0xB << 1)
#define CHRK_SB_PGD_PORT_RX_NUM 16
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 4158e65..f93aba1 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -66,6 +66,7 @@
#define PERFCL_EFUSE_MASK 0x7
#define ENABLE_REG 0x0
+#define ENABLE_OSM BIT(0)
#define FREQ_REG 0x110
#define VOLT_REG 0x114
#define OVERRIDE_REG 0x118
@@ -2629,7 +2630,7 @@
}
/* Check if OSM has been enabled already by trustzone. */
- if (readl_relaxed(l3_clk.vbases[OSM_BASE] + ENABLE_REG)) {
+ if (readl_relaxed(l3_clk.vbases[OSM_BASE] + ENABLE_REG) & ENABLE_OSM) {
dev_info(&pdev->dev, "OSM has been initialized and enabled by TZ software\n");
osm_tz_enabled = true;
}
diff --git a/drivers/media/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c
index 44193f5..2c90e47 100644
--- a/drivers/media/platform/msm/broadcast/tspp.c
+++ b/drivers/media/platform/msm/broadcast/tspp.c
@@ -47,6 +47,7 @@
#include <linux/msm-bus.h>
#include <linux/interrupt.h> /* tasklet */
#include <asm/arch_timer.h> /* Timer */
+#include <linux/dma-buf.h>
/*
* General defines
@@ -495,7 +496,6 @@
struct tspp_pinctrl pinctrl;
unsigned int tts_source; /* Time stamp source type LPASS timer/TCR */
struct dma_iommu_mapping *iommu_mapping;
- bool bypass_s1_smmu;
struct dentry *dent;
struct dentry *debugfs_regs[ARRAY_SIZE(debugfs_tspp_regs)];
@@ -1068,7 +1068,6 @@
static int tspp_iommu_init(struct tspp_device *device)
{
struct dma_iommu_mapping *iommu_map;
- int s1_bypass = 1;
iommu_map = arm_iommu_create_mapping(&platform_bus_type,
TSPP_SMMU_IOVA_START,
@@ -1077,12 +1076,6 @@
dev_err(&device->pdev->dev, "iommu_create_mapping failure\n");
return PTR_ERR(iommu_map);
}
- if (iommu_domain_set_attr(iommu_map->domain,
- DOMAIN_ATTR_S1_BYPASS, &s1_bypass)) {
- dev_err(&device->pdev->dev, "Can't bypass s1 translation\n");
- arm_iommu_release_mapping(iommu_map);
- return -EIO;
- }
if (arm_iommu_attach_device(&device->pdev->dev, iommu_map)) {
dev_err(&device->pdev->dev, "can't arm_iommu_attach_device\n");
arm_iommu_release_mapping(iommu_map);
@@ -1095,7 +1088,7 @@
static void tspp_iommu_release_iomapping(struct tspp_device *device)
{
- if (device->bypass_s1_smmu && device->iommu_mapping)
+ if (device->iommu_mapping)
arm_iommu_release_mapping(device->iommu_mapping);
device->iommu_mapping = NULL;
@@ -1113,7 +1106,7 @@
if (alloc) {
TSPP_DEBUG("tspp using alloc function");
desc->virt_base = alloc(channel_id, size,
- &desc->phys_base, user);
+ &desc->phys_base, &desc->dma_base, user);
} else {
if (!dma_pool)
desc->virt_base = dma_alloc_coherent(NULL, size,
@@ -2605,7 +2598,8 @@
desc->next = channel->data;
/* prepare the sps descriptor */
- desc->sps.phys_base = desc->desc.phys_base;
+ desc->sps.phys_base = ((alloc != NULL) ? desc->desc.dma_base :
+ desc->desc.phys_base);
desc->sps.base = desc->desc.virt_base;
desc->sps.size = desc->desc.size;
@@ -2643,6 +2637,121 @@
}
EXPORT_SYMBOL(tspp_allocate_buffers);
+/**
+ * tspp_attach_ion_dma_buff- attach ion dma buffer to TSPP device
+ * It will attach the DMA buffer to TSPP device to go through SMMU.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @ion_dma_buf: It contains required members for ION buffer dma mapping.
+ *
+ * Return error status
+ *
+ */
+int tspp_attach_ion_dma_buff(u32 dev, struct tspp_ion_dma_buf_info *ion_dma_buf)
+{
+ struct tspp_device *pdev;
+ int dir = DMA_FROM_DEVICE;
+ int ret = -1;
+
+ if (NULL == ion_dma_buf || NULL == ion_dma_buf->dbuf) {
+ pr_err("tspp: invalid input argument");
+ return -EINVAL;
+ }
+
+ if (dev >= TSPP_MAX_DEVICES) {
+ pr_err("tspp: device id out of range");
+ return -ENODEV;
+ }
+
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp: can't find device %i", dev);
+ return -ENODEV;
+ }
+
+ ion_dma_buf->attach = dma_buf_attach(ion_dma_buf->dbuf,
+ &pdev->pdev->dev);
+ if (IS_ERR_OR_NULL(ion_dma_buf->attach)) {
+ dev_err(&pdev->pdev->dev, "%s: dma_buf_attach fail", __func__);
+ return -ENODEV;
+ }
+ ion_dma_buf->table = dma_buf_map_attachment(ion_dma_buf->attach, dir);
+ if (IS_ERR_OR_NULL(ion_dma_buf->table)) {
+ dev_err(&pdev->pdev->dev, "dma_buf_map_attachment fail");
+ dma_buf_detach(ion_dma_buf->dbuf, ion_dma_buf->attach);
+ return -ENODEV;
+ }
+ ret = dma_map_sg(&pdev->pdev->dev, ion_dma_buf->table->sgl,
+ ion_dma_buf->table->nents, dir);
+ if (ret <= 0) {
+ dev_err(&pdev->pdev->dev, "dma_map_sg failed! ret=%d\n", ret);
+ goto unmap_attachment;
+ }
+ if (ion_dma_buf->table->nents > 1) {
+ dev_err(&pdev->pdev->dev, "no of sg table entries %d > 1\n",
+ ion_dma_buf->table->nents);
+ goto unmap_attachment;
+ }
+
+ ion_dma_buf->dma_map_base = sg_dma_address(ion_dma_buf->table->sgl);
+ ion_dma_buf->smmu_map = true;
+ return 0;
+
+unmap_attachment:
+ dma_buf_unmap_attachment(ion_dma_buf->attach, ion_dma_buf->table, dir);
+ dma_buf_detach(ion_dma_buf->dbuf, ion_dma_buf->attach);
+ dma_buf_put(ion_dma_buf->dbuf);
+
+ return ret;
+}
+EXPORT_SYMBOL(tspp_attach_ion_dma_buff);
+
+/**
+ * tspp_detach_ion_dma_buff - detach the mapped ion dma buffer from TSPP device
+ * It will detach previously mapped DMA buffer from TSPP device.
+ *
+ * @dev: TSPP device (up to TSPP_MAX_DEVICES)
+ * @ion_dma_buf: It contains required members for ION buffer dma mapping.
+ *
+ * Return error status
+ *
+ */
+int tspp_detach_ion_dma_buff(u32 dev, struct tspp_ion_dma_buf_info *ion_dma_buf)
+{
+ struct tspp_device *pdev;
+ int dir = DMA_FROM_DEVICE;
+
+ if (ion_dma_buf == NULL || ion_dma_buf->dbuf == NULL ||
+ ion_dma_buf->table == NULL || ion_dma_buf->table->sgl == NULL ||
+ ion_dma_buf->smmu_map == false) {
+ pr_err("tspp: invalid input argument");
+ return -EINVAL;
+ }
+
+ if (dev >= TSPP_MAX_DEVICES) {
+ pr_err("tspp: device id out of range");
+ return -ENODEV;
+ }
+
+ pdev = tspp_find_by_id(dev);
+ if (!pdev) {
+ pr_err("tspp: can't find device %i", dev);
+ return -ENODEV;
+ }
+
+
+ dma_unmap_sg(&pdev->pdev->dev, ion_dma_buf->table->sgl,
+ ion_dma_buf->table->nents, dir);
+ dma_buf_unmap_attachment(ion_dma_buf->attach, ion_dma_buf->table, dir);
+ dma_buf_detach(ion_dma_buf->dbuf, ion_dma_buf->attach);
+ dma_buf_put(ion_dma_buf->dbuf);
+
+ ion_dma_buf->smmu_map = false;
+ return 0;
+}
+EXPORT_SYMBOL(tspp_detach_ion_dma_buff);
+
+
/*** debugfs ***/
static int debugfs_iomem_x32_set(void *data, u64 val)
{
@@ -3002,12 +3111,9 @@
goto err_irq;
device->req_irqs = false;
- if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass")) {
- device->bypass_s1_smmu = true;
- if (tspp_iommu_init(device)) {
- dev_err(&pdev->dev, "iommu init failed");
- goto err_iommu;
- }
+ if (tspp_iommu_init(device)) {
+ dev_err(&pdev->dev, "iommu init failed");
+ goto err_iommu;
}
device->tts_source = TSIF_TTS_TCR;
@@ -3152,6 +3258,9 @@
if (device->tsif_vreg)
regulator_disable(device->tsif_vreg);
+ tspp_iommu_release_iomapping(device);
+ arm_iommu_detach_device(&pdev->dev);
+
pm_runtime_disable(&pdev->dev);
kfree(device);
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
index da7ecce..a075ed9 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v1.c
@@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/vmalloc.h>
+#include <linux/dma-buf.h>
#include <linux/qcom_tspp.h>
#include "mpq_dvb_debug.h"
#include "mpq_dmx_plugin_common.h"
@@ -189,6 +190,10 @@
/* Mutex protecting the data-structure */
struct mutex mutex;
+
+ /* ion dma buffer mapping structure */
+ struct tspp_ion_dma_buf_info ch_ion_dma_buf;
+
} tsif[TSIF_COUNT];
/* ION client used for TSPP data buffer allocation */
@@ -196,7 +201,8 @@
} mpq_dmx_tspp_info;
static void *tspp_mem_allocator(int channel_id, u32 size,
- phys_addr_t *phys_base, void *user)
+ phys_addr_t *phys_base, dma_addr_t *dma_base,
+ void *user)
{
void *virt_addr = NULL;
int i = TSPP_GET_TSIF_NUM(channel_id);
@@ -213,6 +219,10 @@
(mpq_dmx_tspp_info.tsif[i].ch_mem_heap_phys_base +
(mpq_dmx_tspp_info.tsif[i].buff_index * size));
+ *dma_base =
+ (mpq_dmx_tspp_info.tsif[i].ch_ion_dma_buf.dma_map_base +
+ (mpq_dmx_tspp_info.tsif[i].buff_index * size));
+
mpq_dmx_tspp_info.tsif[i].buff_index++;
return virt_addr;
@@ -539,6 +549,9 @@
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_virt_base = NULL;
mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle = NULL;
+
+ tspp_detach_ion_dma_buff(0,
+ &mpq_dmx_tspp_info.tsif[tsif].ch_ion_dma_buf);
}
/**
@@ -589,6 +602,24 @@
return -ENOMEM;
}
+ mpq_dmx_tspp_info.tsif[tsif].ch_ion_dma_buf.dbuf = ion_share_dma_buf(
+ mpq_dmx_tspp_info.ion_client,
+ mpq_dmx_tspp_info.tsif[tsif].ch_mem_heap_handle);
+ if (IS_ERR_OR_NULL(mpq_dmx_tspp_info.tsif[tsif].ch_ion_dma_buf.dbuf)) {
+ MPQ_DVB_ERR_PRINT("%s: ion_share_dma_buf failed\n", __func__);
+ mpq_dmx_channel_mem_free(tsif);
+ return -ENOMEM;
+ }
+
+ result = tspp_attach_ion_dma_buff(0,
+ &mpq_dmx_tspp_info.tsif[tsif].ch_ion_dma_buf);
+ if (result) {
+ MPQ_DVB_ERR_PRINT("%s: tspp_attach_ion_dma_buff failed\n",
+ __func__);
+ mpq_dmx_channel_mem_free(tsif);
+ return result;
+ }
+
return 0;
}
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 7c77280..c1e77aa 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -691,7 +691,7 @@
struct se_geni_rsc *rsc)
{
unsigned long flags;
- struct se_geni_rsc *tmp;
+ struct se_geni_rsc *tmp = NULL;
struct list_head *ins_list_head;
bool bus_bw_update = false;
int ret = 0;
@@ -709,7 +709,7 @@
list_add(&rsc->ib_list, ins_list_head);
/* Currently inserted node has greater average BW value */
if (ins_list_head == &geni_se_dev->ib_list_head)
- geni_se_dev->cur_ib = tmp->ib;
+ geni_se_dev->cur_ib = rsc->ib;
bus_bw_update = geni_se_check_bus_bw(geni_se_dev);
spin_unlock_irqrestore(&geni_se_dev->ab_ib_lock, flags);
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 56f3b1e..7c94744 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -75,6 +75,8 @@
#define ESR_TIMER_CHG_MAX_OFFSET 0
#define ESR_TIMER_CHG_INIT_WORD 18
#define ESR_TIMER_CHG_INIT_OFFSET 2
+#define ESR_EXTRACTION_ENABLE_WORD 19
+#define ESR_EXTRACTION_ENABLE_OFFSET 0
#define PROFILE_LOAD_WORD 24
#define PROFILE_LOAD_OFFSET 0
#define ESR_RSLOW_DISCHG_WORD 34
@@ -3033,6 +3035,89 @@
return 0;
}
+static int fg_force_esr_meas(struct fg_chip *chip)
+{
+ int rc;
+ int esr_uohms;
+
+ /* force esr extraction enable */
+ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
+ ESR_EXTRACTION_ENABLE_OFFSET, BIT(0), BIT(0),
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("failed to enable esr extn rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip),
+ LD_REG_CTRL_BIT, 0);
+ if (rc < 0) {
+ pr_err("Error in configuring qnovo_cfg rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_masked_write(chip, BATT_INFO_TM_MISC1(chip),
+ ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT,
+ ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT);
+ if (rc < 0) {
+ pr_err("Error in configuring force ESR rc=%d\n", rc);
+ return rc;
+ }
+
+ /* wait 1.5 seconds for hw to measure ESR */
+ msleep(1500);
+ rc = fg_masked_write(chip, BATT_INFO_TM_MISC1(chip),
+ ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT,
+ 0);
+ if (rc < 0) {
+ pr_err("Error in restoring force ESR rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip),
+ LD_REG_CTRL_BIT, LD_REG_CTRL_BIT);
+ if (rc < 0) {
+ pr_err("Error in restoring qnovo_cfg rc=%d\n", rc);
+ return rc;
+ }
+
+ /* force esr extraction disable */
+ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
+ ESR_EXTRACTION_ENABLE_OFFSET, BIT(0), 0,
+ FG_IMA_DEFAULT);
+ if (rc < 0) {
+ pr_err("failed to disable esr extn rc=%d\n", rc);
+ return rc;
+ }
+
+ fg_get_battery_resistance(chip, &esr_uohms);
+ fg_dbg(chip, FG_STATUS, "ESR uohms = %d\n", esr_uohms);
+
+ return rc;
+}
+
+static int fg_prepare_for_qnovo(struct fg_chip *chip, int qnovo_enable)
+{
+ int rc;
+
+ /* force esr extraction disable when qnovo enables */
+ rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
+ ESR_EXTRACTION_ENABLE_OFFSET,
+ BIT(0), qnovo_enable ? 0 : BIT(0),
+ FG_IMA_DEFAULT);
+ if (rc < 0)
+ pr_err("Error in configuring esr extraction rc=%d\n", rc);
+
+ rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip),
+ LD_REG_CTRL_BIT,
+ qnovo_enable ? LD_REG_CTRL_BIT : 0);
+ if (rc < 0) {
+ pr_err("Error in configuring qnovo_cfg rc=%d\n", rc);
+ return rc;
+ }
+ fg_dbg(chip, FG_STATUS, "Prepared for Qnovo\n");
+ return 0;
+}
/* PSY CALLBACKS STAY HERE */
static int fg_psy_get_property(struct power_supply *psy,
@@ -3141,6 +3226,12 @@
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
rc = fg_set_constant_chg_voltage(chip, pval->intval);
break;
+ case POWER_SUPPLY_PROP_RESISTANCE:
+ rc = fg_force_esr_meas(chip);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE:
+ rc = fg_prepare_for_qnovo(chip, pval->intval);
+ break;
default:
break;
}
diff --git a/drivers/power/supply/qcom/qpnp-qnovo.c b/drivers/power/supply/qcom/qpnp-qnovo.c
index eb97eb0..cf90f90 100644
--- a/drivers/power/supply/qcom/qpnp-qnovo.c
+++ b/drivers/power/supply/qcom/qpnp-qnovo.c
@@ -20,6 +20,7 @@
#include <linux/of_irq.h>
#include <linux/qpnp/qpnp-revid.h>
#include <linux/pmic-voter.h>
+#include <linux/delay.h>
#define QNOVO_REVISION1 0x00
#define QNOVO_REVISION2 0x01
@@ -114,6 +115,17 @@
#define OK_TO_QNOVO_VOTER "ok_to_qnovo_voter"
#define QNOVO_VOTER "qnovo_voter"
+#define FG_AVAILABLE_VOTER "FG_AVAILABLE_VOTER"
+#define QNOVO_OVERALL_VOTER "QNOVO_OVERALL_VOTER"
+#define QNI_PT_VOTER "QNI_PT_VOTER"
+#define ESR_VOTER "ESR_VOTER"
+
+#define HW_OK_TO_QNOVO_VOTER "HW_OK_TO_QNOVO_VOTER"
+#define CHG_READY_VOTER "CHG_READY_VOTER"
+#define USB_READY_VOTER "USB_READY_VOTER"
+#define DC_READY_VOTER "DC_READY_VOTER"
+
+#define PT_RESTART_VOTER "PT_RESTART_VOTER"
struct qnovo_dt_props {
bool external_rsense;
@@ -127,6 +139,10 @@
struct qnovo_dt_props dt;
struct device *dev;
struct votable *disable_votable;
+ struct votable *pt_dis_votable;
+ struct votable *not_ok_to_qnovo_votable;
+ struct votable *chg_ready_votable;
+ struct votable *awake_votable;
struct class qnovo_class;
struct pmic_revid_data *pmic_rev_id;
u32 wa_flags;
@@ -138,10 +154,18 @@
s64 v_gain_mega;
struct notifier_block nb;
struct power_supply *batt_psy;
+ struct power_supply *bms_psy;
+ struct power_supply *usb_psy;
+ struct power_supply *dc_psy;
struct work_struct status_change_work;
int fv_uV_request;
int fcc_uA_request;
- bool ok_to_qnovo;
+ int usb_present;
+ int dc_present;
+ struct delayed_work usb_debounce_work;
+ struct delayed_work dc_debounce_work;
+
+ struct delayed_work ptrain_restart_work;
};
static int debug_mask;
@@ -229,6 +253,39 @@
return true;
}
+static bool is_fg_available(struct qnovo *chip)
+{
+ if (!chip->bms_psy)
+ chip->bms_psy = power_supply_get_by_name("bms");
+
+ if (!chip->bms_psy)
+ return false;
+
+ return true;
+}
+
+static bool is_usb_available(struct qnovo *chip)
+{
+ if (!chip->usb_psy)
+ chip->usb_psy = power_supply_get_by_name("usb");
+
+ if (!chip->usb_psy)
+ return false;
+
+ return true;
+}
+
+static bool is_dc_available(struct qnovo *chip)
+{
+ if (!chip->dc_psy)
+ chip->dc_psy = power_supply_get_by_name("dc");
+
+ if (!chip->dc_psy)
+ return false;
+
+ return true;
+}
+
static int qnovo_batt_psy_update(struct qnovo *chip, bool disable)
{
union power_supply_propval pval = {0};
@@ -281,10 +338,86 @@
return -EINVAL;
}
+ /*
+ * fg must be available for enable FG_AVAILABLE_VOTER
+ * won't enable it otherwise
+ */
+
+ if (is_fg_available(chip))
+ power_supply_set_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE,
+ &pval);
+
+ vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, disable, 0);
rc = qnovo_batt_psy_update(chip, disable);
return rc;
}
+static int pt_dis_votable_cb(struct votable *votable, void *data, int disable,
+ const char *client)
+{
+ struct qnovo *chip = data;
+ int rc;
+
+ if (disable) {
+ cancel_delayed_work_sync(&chip->ptrain_restart_work);
+ vote(chip->awake_votable, PT_RESTART_VOTER, false, 0);
+ }
+
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
+ (bool)disable ? 0 : QNOVO_PTRAIN_EN_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
+ (bool)disable ? "disable" : "enable", rc);
+ return rc;
+ }
+
+ if (!disable) {
+ vote(chip->awake_votable, PT_RESTART_VOTER, true, 0);
+ schedule_delayed_work(&chip->ptrain_restart_work,
+ msecs_to_jiffies(20));
+ }
+
+ return 0;
+}
+
+static int not_ok_to_qnovo_cb(struct votable *votable, void *data,
+ int not_ok_to_qnovo,
+ const char *client)
+{
+ struct qnovo *chip = data;
+
+ vote(chip->disable_votable, OK_TO_QNOVO_VOTER, not_ok_to_qnovo, 0);
+ if (not_ok_to_qnovo)
+ vote(chip->disable_votable, USER_VOTER, true, 0);
+
+ kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
+ return 0;
+}
+
+static int chg_ready_cb(struct votable *votable, void *data, int ready,
+ const char *client)
+{
+ struct qnovo *chip = data;
+
+ vote(chip->not_ok_to_qnovo_votable, CHG_READY_VOTER, !ready, 0);
+
+ return 0;
+}
+
+static int awake_cb(struct votable *votable, void *data, int awake,
+ const char *client)
+{
+ struct qnovo *chip = data;
+
+ if (awake)
+ pm_stay_awake(chip->dev);
+ else
+ pm_relax(chip->dev);
+
+ return 0;
+}
+
static int qnovo_parse_dt(struct qnovo *chip)
{
struct device_node *node = chip->dev->of_node;
@@ -626,8 +759,9 @@
char *buf)
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
+ int val = get_effective_result(chip->not_ok_to_qnovo_votable);
- return snprintf(buf, PAGE_SIZE, "%d\n", chip->ok_to_qnovo);
+ return snprintf(buf, PAGE_SIZE, "%d\n", !val);
}
static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr,
@@ -656,21 +790,10 @@
static ssize_t pt_enable_show(struct class *c, struct class_attribute *attr,
char *ubuf)
{
- int i = attr - qnovo_attributes;
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
- u8 buf[2] = {0, 0};
- u16 regval;
- int rc;
+ int val = get_effective_result(chip->pt_dis_votable);
- rc = qnovo_read(chip, params[i].start_addr, buf, params[i].num_regs);
- if (rc < 0) {
- pr_err("Couldn't read %s rc = %d\n", params[i].name, rc);
- return -EINVAL;
- }
- regval = buf[1] << 8 | buf[0];
-
- return snprintf(ubuf, PAGE_SIZE, "%d\n",
- (int)(regval & QNOVO_PTRAIN_EN_BIT));
+ return snprintf(ubuf, PAGE_SIZE, "%d\n", !val);
}
static ssize_t pt_enable_store(struct class *c, struct class_attribute *attr,
@@ -678,21 +801,12 @@
{
struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
unsigned long val;
- int rc = 0;
-
- if (get_effective_result(chip->disable_votable))
- return -EINVAL;
if (kstrtoul(ubuf, 0, &val))
return -EINVAL;
- rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
- (bool)val ? QNOVO_PTRAIN_EN_BIT : 0);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't %s pulse train rc=%d\n",
- (bool)val ? "enable" : "disable", rc);
- return rc;
- }
+ /* val being 0, userspace wishes to disable pt so vote true */
+ vote(chip->pt_dis_votable, QNI_PT_VOTER, val ? false : true, 0);
return count;
}
@@ -1116,41 +1230,146 @@
{
u8 val = 0;
int rc;
- bool ok_to_qnovo;
- bool changed = false;
+ bool hw_ok_to_qnovo;
rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1);
if (rc < 0) {
pr_err("Couldn't read error sts rc = %d\n", rc);
- ok_to_qnovo = false;
+ hw_ok_to_qnovo = false;
} else {
/*
* For CV mode keep qnovo enabled, userspace is expected to
* disable it after few runs
*/
- ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ? true : false;
+ hw_ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ?
+ true : false;
}
- if (chip->ok_to_qnovo ^ ok_to_qnovo) {
-
- vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !ok_to_qnovo, 0);
- if (!ok_to_qnovo)
- vote(chip->disable_votable, USER_VOTER, true, 0);
-
- chip->ok_to_qnovo = ok_to_qnovo;
- changed = true;
- }
-
- return changed;
+ vote(chip->not_ok_to_qnovo_votable, HW_OK_TO_QNOVO_VOTER,
+ !hw_ok_to_qnovo, 0);
+ return 0;
}
+static void usb_debounce_work(struct work_struct *work)
+{
+ struct qnovo *chip = container_of(work,
+ struct qnovo, usb_debounce_work.work);
+
+ vote(chip->chg_ready_votable, USB_READY_VOTER, true, 0);
+ vote(chip->awake_votable, USB_READY_VOTER, false, 0);
+}
+
+static void dc_debounce_work(struct work_struct *work)
+{
+ struct qnovo *chip = container_of(work,
+ struct qnovo, dc_debounce_work.work);
+
+ vote(chip->chg_ready_votable, DC_READY_VOTER, true, 0);
+ vote(chip->awake_votable, DC_READY_VOTER, false, 0);
+}
+
+#define DEBOUNCE_MS 15000 /* 15 seconds */
static void status_change_work(struct work_struct *work)
{
struct qnovo *chip = container_of(work,
struct qnovo, status_change_work);
+ union power_supply_propval pval;
+ bool usb_present = false, dc_present = false;
+ int rc;
- if (qnovo_update_status(chip))
- kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
+ if (is_fg_available(chip))
+ vote(chip->disable_votable, FG_AVAILABLE_VOTER, false, 0);
+
+ if (is_usb_available(chip)) {
+ rc = power_supply_get_property(chip->usb_psy,
+ POWER_SUPPLY_PROP_PRESENT, &pval);
+ usb_present = (rc < 0) ? 0 : pval.intval;
+ }
+
+ if (chip->usb_present && !usb_present) {
+ /* removal */
+ chip->usb_present = 0;
+ cancel_delayed_work_sync(&chip->usb_debounce_work);
+ vote(chip->awake_votable, USB_READY_VOTER, false, 0);
+ vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0);
+ } else if (!chip->usb_present && usb_present) {
+ /* insertion */
+ chip->usb_present = 1;
+ vote(chip->awake_votable, USB_READY_VOTER, true, 0);
+ schedule_delayed_work(&chip->usb_debounce_work,
+ msecs_to_jiffies(DEBOUNCE_MS));
+ }
+
+ if (is_dc_available(chip)) {
+ rc = power_supply_get_property(chip->dc_psy,
+ POWER_SUPPLY_PROP_PRESENT,
+ &pval);
+ dc_present = (rc < 0) ? 0 : pval.intval;
+ }
+
+ if (usb_present)
+ dc_present = 0;
+
+ if (chip->dc_present && !dc_present) {
+ /* removal */
+ chip->dc_present = 0;
+ cancel_delayed_work_sync(&chip->dc_debounce_work);
+ vote(chip->awake_votable, DC_READY_VOTER, false, 0);
+ vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0);
+ } else if (!chip->dc_present && dc_present) {
+ /* insertion */
+ chip->dc_present = 1;
+ vote(chip->awake_votable, DC_READY_VOTER, true, 0);
+ schedule_delayed_work(&chip->dc_debounce_work,
+ msecs_to_jiffies(DEBOUNCE_MS));
+ }
+
+ qnovo_update_status(chip);
+}
+
+static void ptrain_restart_work(struct work_struct *work)
+{
+ struct qnovo *chip = container_of(work,
+ struct qnovo, ptrain_restart_work.work);
+ u8 pt_t1, pt_t2;
+ int rc;
+
+ rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t1, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n",
+ rc);
+ goto clean_up;
+ }
+
+ /* pttime increments every 2 seconds */
+ msleep(2100);
+
+ rc = qnovo_read(chip, QNOVO_PTTIME_STS, &pt_t2, 1);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read QNOVO_PTTIME_STS rc = %d\n",
+ rc);
+ goto clean_up;
+ }
+
+ if (pt_t1 != pt_t2)
+ goto clean_up;
+
+ /* Toggle pt enable to restart pulse train */
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT, 0);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't disable pulse train rc=%d\n", rc);
+ goto clean_up;
+ }
+ msleep(1000);
+ rc = qnovo_masked_write(chip, QNOVO_PTRAIN_EN, QNOVO_PTRAIN_EN_BIT,
+ QNOVO_PTRAIN_EN_BIT);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't enable pulse train rc=%d\n", rc);
+ goto clean_up;
+ }
+
+clean_up:
+ vote(chip->awake_votable, PT_RESTART_VOTER, false, 0);
}
static int qnovo_notifier_call(struct notifier_block *nb,
@@ -1162,7 +1381,10 @@
if (ev != PSY_EVENT_PROP_CHANGED)
return NOTIFY_OK;
- if (strcmp(psy->desc->name, "battery") == 0)
+ if (strcmp(psy->desc->name, "battery") == 0
+ || strcmp(psy->desc->name, "bms") == 0
+ || strcmp(psy->desc->name, "usb") == 0
+ || strcmp(psy->desc->name, "dc") == 0)
schedule_work(&chip->status_change_work);
return NOTIFY_OK;
@@ -1171,7 +1393,23 @@
static irqreturn_t handle_ptrain_done(int irq, void *data)
{
struct qnovo *chip = data;
+ union power_supply_propval pval = {0};
+ /*
+ * hw resets pt_en bit once ptrain_done triggers.
+ * vote on behalf of QNI to disable it such that
+ * once QNI enables it, the votable state changes
+ * and the callback that sets it is indeed invoked
+ */
+ vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0);
+
+ vote(chip->pt_dis_votable, ESR_VOTER, true, 0);
+ if (is_fg_available(chip))
+ power_supply_set_property(chip->bms_psy,
+ POWER_SUPPLY_PROP_RESISTANCE,
+ &pval);
+
+ vote(chip->pt_dis_votable, ESR_VOTER, false, 0);
qnovo_update_status(chip);
kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
return IRQ_HANDLED;
@@ -1186,6 +1424,11 @@
u8 val;
vote(chip->disable_votable, USER_VOTER, true, 0);
+ vote(chip->disable_votable, FG_AVAILABLE_VOTER, true, 0);
+
+ vote(chip->pt_dis_votable, QNI_PT_VOTER, true, 0);
+ vote(chip->pt_dis_votable, QNOVO_OVERALL_VOTER, true, 0);
+ vote(chip->pt_dis_votable, ESR_VOTER, false, 0);
val = 0;
rc = qnovo_write(chip, QNOVO_STRM_CTRL, &val, 1);
@@ -1349,12 +1592,45 @@
goto cleanup;
}
+ chip->pt_dis_votable = create_votable("QNOVO_PT_DIS", VOTE_SET_ANY,
+ pt_dis_votable_cb, chip);
+ if (IS_ERR(chip->pt_dis_votable)) {
+ rc = PTR_ERR(chip->pt_dis_votable);
+ goto destroy_disable_votable;
+ }
+
+ chip->not_ok_to_qnovo_votable = create_votable("QNOVO_NOT_OK",
+ VOTE_SET_ANY,
+ not_ok_to_qnovo_cb, chip);
+ if (IS_ERR(chip->not_ok_to_qnovo_votable)) {
+ rc = PTR_ERR(chip->not_ok_to_qnovo_votable);
+ goto destroy_pt_dis_votable;
+ }
+
+ chip->chg_ready_votable = create_votable("QNOVO_CHG_READY",
+ VOTE_SET_ANY,
+ chg_ready_cb, chip);
+ if (IS_ERR(chip->chg_ready_votable)) {
+ rc = PTR_ERR(chip->chg_ready_votable);
+ goto destroy_not_ok_to_qnovo_votable;
+ }
+
+ chip->awake_votable = create_votable("QNOVO_AWAKE", VOTE_SET_ANY,
+ awake_cb, chip);
+ if (IS_ERR(chip->awake_votable)) {
+ rc = PTR_ERR(chip->awake_votable);
+ goto destroy_chg_ready_votable;
+ }
+
INIT_WORK(&chip->status_change_work, status_change_work);
+ INIT_DELAYED_WORK(&chip->dc_debounce_work, dc_debounce_work);
+ INIT_DELAYED_WORK(&chip->usb_debounce_work, usb_debounce_work);
+ INIT_DELAYED_WORK(&chip->ptrain_restart_work, ptrain_restart_work);
rc = qnovo_hw_init(chip);
if (rc < 0) {
pr_err("Couldn't initialize hardware rc=%d\n", rc);
- goto destroy_votable;
+ goto destroy_awake_votable;
}
rc = qnovo_register_notifier(chip);
@@ -1390,7 +1666,15 @@
unreg_notifier:
power_supply_unreg_notifier(&chip->nb);
-destroy_votable:
+destroy_awake_votable:
+ destroy_votable(chip->awake_votable);
+destroy_chg_ready_votable:
+ destroy_votable(chip->chg_ready_votable);
+destroy_not_ok_to_qnovo_votable:
+ destroy_votable(chip->not_ok_to_qnovo_votable);
+destroy_pt_dis_votable:
+ destroy_votable(chip->pt_dis_votable);
+destroy_disable_votable:
destroy_votable(chip->disable_votable);
cleanup:
platform_set_drvdata(pdev, NULL);
@@ -1403,6 +1687,9 @@
class_unregister(&chip->qnovo_class);
power_supply_unreg_notifier(&chip->nb);
+ destroy_votable(chip->chg_ready_votable);
+ destroy_votable(chip->not_ok_to_qnovo_votable);
+ destroy_votable(chip->pt_dis_votable);
destroy_votable(chip->disable_votable);
platform_set_drvdata(pdev, NULL);
return 0;
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 5605c8a..b3de8d0 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -210,6 +210,9 @@
chg->step_chg_enabled = of_property_read_bool(node,
"qcom,step-charging-enable");
+ chg->sw_jeita_enabled = of_property_read_bool(node,
+ "qcom,sw-jeita-enable");
+
rc = of_property_read_u32(node, "qcom,wd-bark-time-secs",
&chip->dt.wd_bark_time);
if (rc < 0 || chip->dt.wd_bark_time < MIN_WD_BARK_TIME)
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 57a85de..5ae653e 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -1570,8 +1570,8 @@
union power_supply_propval *val)
{
union power_supply_propval pval = {0, };
- bool usb_online, dc_online;
- u8 stat;
+ bool usb_online, dc_online, qnovo_en;
+ u8 stat, pt_en_cmd;
int rc;
rc = smblib_get_prop_usb_online(chg, &pval);
@@ -1639,11 +1639,22 @@
smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
rc);
return rc;
- }
+ }
stat &= ENABLE_TRICKLE_BIT | ENABLE_PRE_CHARGING_BIT |
ENABLE_FAST_CHARGING_BIT | ENABLE_FULLON_MODE_BIT;
- if (!stat)
+
+ rc = smblib_read(chg, QNOVO_PT_ENABLE_CMD_REG, &pt_en_cmd);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read QNOVO_PT_ENABLE_CMD_REG rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ qnovo_en = (bool)(pt_en_cmd & QNOVO_PT_ENABLE_CMD_BIT);
+
+ /* ignore stat7 when qnovo is enabled */
+ if (!qnovo_en && !stat)
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
return 0;
@@ -2370,16 +2381,9 @@
int smblib_get_prop_input_voltage_settled(struct smb_charger *chg,
union power_supply_propval *val)
{
- const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
int rc, pulses;
- val->intval = MICRO_5V;
- if (apsd_result == NULL) {
- smblib_err(chg, "APSD result is NULL\n");
- return 0;
- }
-
- switch (apsd_result->pst) {
+ switch (chg->real_charger_type) {
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
rc = smblib_get_pulse_cnt(chg, &pulses);
if (rc < 0) {
@@ -2389,6 +2393,9 @@
}
val->intval = MICRO_5V + HVDCP3_STEP_UV * pulses;
break;
+ case POWER_SUPPLY_TYPE_USB_PD:
+ val->intval = chg->voltage_min_uv;
+ break;
default:
val->intval = MICRO_5V;
break;
@@ -2636,6 +2643,7 @@
}
chg->voltage_min_uv = min_uv;
+ power_supply_changed(chg->usb_main_psy);
return rc;
}
@@ -4083,7 +4091,7 @@
if (rc < 0)
smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc);
- if (chg->step_chg_enabled)
+ if (chg->step_chg_enabled || chg->sw_jeita_enabled)
power_supply_changed(chg->batt_psy);
return IRQ_HANDLED;
@@ -4721,7 +4729,8 @@
return rc;
}
- rc = qcom_step_chg_init(chg->step_chg_enabled);
+ rc = qcom_step_chg_init(chg->step_chg_enabled,
+ chg->sw_jeita_enabled);
if (rc < 0) {
smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n",
rc);
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 4ffbeb6..5b59597 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -308,6 +308,7 @@
int dcp_icl_ua;
int fake_capacity;
bool step_chg_enabled;
+ bool sw_jeita_enabled;
bool is_hdc;
bool chg_done;
bool micro_usb_mode;
diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c
index a2c08be..053aac3 100644
--- a/drivers/power/supply/qcom/step-chg-jeita.c
+++ b/drivers/power/supply/qcom/step-chg-jeita.c
@@ -20,7 +20,7 @@
#define MAX_STEP_CHG_ENTRIES 8
#define STEP_CHG_VOTER "STEP_CHG_VOTER"
-#define STATUS_CHANGE_VOTER "STATUS_CHANGE_VOTER"
+#define JEITA_VOTER "JEITA_VOTER"
#define is_between(left, right, value) \
(((left) >= (right) && (left) >= (value) \
@@ -28,23 +28,44 @@
|| ((left) <= (right) && (left) <= (value) \
&& (value) <= (right)))
-struct step_chg_data {
- u32 vbatt_soc_low;
- u32 vbatt_soc_high;
- u32 fcc_ua;
+struct range_data {
+ u32 low_threshold;
+ u32 high_threshold;
+ u32 value;
};
struct step_chg_cfg {
- u32 psy_prop;
- char *prop_name;
- struct step_chg_data cfg[MAX_STEP_CHG_ENTRIES];
+ u32 psy_prop;
+ char *prop_name;
+ int hysteresis;
+ struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES];
+};
+
+struct jeita_fcc_cfg {
+ u32 psy_prop;
+ char *prop_name;
+ int hysteresis;
+ struct range_data fcc_cfg[MAX_STEP_CHG_ENTRIES];
+};
+
+struct jeita_fv_cfg {
+ u32 psy_prop;
+ char *prop_name;
+ int hysteresis;
+ struct range_data fv_cfg[MAX_STEP_CHG_ENTRIES];
};
struct step_chg_info {
- ktime_t last_update_time;
+ ktime_t step_last_update_time;
+ ktime_t jeita_last_update_time;
bool step_chg_enable;
+ bool sw_jeita_enable;
+ int jeita_fcc_index;
+ int jeita_fv_index;
+ int step_index;
struct votable *fcc_votable;
+ struct votable *fv_votable;
struct wakeup_source *step_chg_ws;
struct power_supply *batt_psy;
struct delayed_work status_change_work;
@@ -53,32 +74,70 @@
static struct step_chg_info *the_chip;
+#define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */
+
/*
* Step Charging Configuration
* Update the table based on the battery profile
* Supports VBATT and SOC based source
+ * range data must be in increasing ranges and shouldn't overlap
*/
static struct step_chg_cfg step_chg_config = {
- .psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW,
- .prop_name = "VBATT",
- .cfg = {
+ .psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ .prop_name = "VBATT",
+ .hysteresis = 100000, /* 100mV */
+ .fcc_cfg = {
/* VBAT_LOW VBAT_HIGH FCC */
{3600000, 4000000, 3000000},
- {4000000, 4200000, 2800000},
- {4200000, 4400000, 2000000},
+ {4001000, 4200000, 2800000},
+ {4201000, 4400000, 2000000},
},
+ /*
+ * SOC STEP-CHG configuration example.
+ *
+ * .psy_prop = POWER_SUPPLY_PROP_CAPACITY,
+ * .prop_name = "SOC",
+ * .fcc_cfg = {
+ * //SOC_LOW SOC_HIGH FCC
+ * {20, 70, 3000000},
+ * {70, 90, 2750000},
+ * {90, 100, 2500000},
+ * },
+ */
+};
+
/*
- * SOC STEP-CHG configuration example.
- *
- * .psy_prop = POWER_SUPPLY_PROP_CAPACITY,
- * .prop_name = "SOC",
- * .cfg = {
- * //SOC_LOW SOC_HIGH FCC
- * {20, 70, 3000000},
- * {70, 90, 2750000},
- * {90, 100, 2500000},
- * },
+ * Jeita Charging Configuration
+ * Update the table based on the battery profile
+ * Please ensure that the TEMP ranges are programmed in the hw so that
+ * an interrupt is issued and a consequent psy changed will cause us to
+ * react immediately.
+ * range data must be in increasing ranges and shouldn't overlap.
+ * Gaps are okay
*/
+static struct jeita_fcc_cfg jeita_fcc_config = {
+ .psy_prop = POWER_SUPPLY_PROP_TEMP,
+ .prop_name = "BATT_TEMP",
+ .hysteresis = 10, /* 1degC hysteresis */
+ .fcc_cfg = {
+ /* TEMP_LOW TEMP_HIGH FCC */
+ {0, 100, 600000},
+ {101, 200, 2000000},
+ {201, 450, 3000000},
+ {451, 550, 600000},
+ },
+};
+
+static struct jeita_fv_cfg jeita_fv_config = {
+ .psy_prop = POWER_SUPPLY_PROP_TEMP,
+ .prop_name = "BATT_TEMP",
+ .hysteresis = 10, /* 1degC hysteresis */
+ .fv_cfg = {
+ /* TEMP_LOW TEMP_HIGH FCC */
+ {0, 100, 4200000},
+ {101, 450, 4400000},
+ {451, 550, 4200000},
+ },
};
static bool is_batt_available(struct step_chg_info *chip)
@@ -92,22 +151,67 @@
return true;
}
-static int get_fcc(int threshold)
+static int get_val(struct range_data *range, int hysteresis, int current_index,
+ int threshold,
+ int *new_index, int *val)
{
int i;
+ *new_index = -EINVAL;
+ /* first find the matching index without hysteresis */
for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
- if (is_between(step_chg_config.cfg[i].vbatt_soc_low,
- step_chg_config.cfg[i].vbatt_soc_high, threshold))
- return step_chg_config.cfg[i].fcc_ua;
+ if (is_between(range[i].low_threshold,
+ range[i].high_threshold, threshold)) {
+ *new_index = i;
+ *val = range[i].value;
+ }
- return -ENODATA;
+ /* if nothing was found, return -ENODATA */
+ if (*new_index == -EINVAL)
+ return -ENODATA;
+ /*
+ * If we don't have a current_index return this
+ * newfound value. There is no hysterisis from out of range
+ * to in range transition
+ */
+ if (current_index == -EINVAL)
+ return 0;
+
+ /*
+ * Check for hysteresis if it in the neighbourhood
+ * of our current index.
+ */
+ if (*new_index == current_index + 1) {
+ if (threshold < range[*new_index].low_threshold + hysteresis) {
+ /*
+ * Stay in the current index, threshold is not higher
+ * by hysteresis amount
+ */
+ *new_index = current_index;
+ *val = range[current_index].value;
+ }
+ } else if (*new_index == current_index - 1) {
+ if (threshold > range[*new_index].high_threshold - hysteresis) {
+ /*
+ * stay in the current index, threshold is not lower
+ * by hysteresis amount
+ */
+ *new_index = current_index;
+ *val = range[current_index].value;
+ }
+ }
+ return 0;
}
static int handle_step_chg_config(struct step_chg_info *chip)
{
union power_supply_propval pval = {0, };
int rc = 0, fcc_ua = 0;
+ u64 elapsed_us;
+
+ elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time);
+ if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
+ goto reschedule;
rc = power_supply_get_property(chip->batt_psy,
POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, &pval);
@@ -119,7 +223,7 @@
if (!chip->step_chg_enable) {
if (chip->fcc_votable)
vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
- return 0;
+ goto update_time;
}
rc = power_supply_get_property(chip->batt_psy,
@@ -130,48 +234,144 @@
return rc;
}
- chip->fcc_votable = find_votable("FCC");
+ rc = get_val(step_chg_config.fcc_cfg, step_chg_config.hysteresis,
+ chip->step_index,
+ pval.intval,
+ &chip->step_index,
+ &fcc_ua);
+ if (rc < 0) {
+ /* remove the vote if no step-based fcc is found */
+ if (chip->fcc_votable)
+ vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
+ goto update_time;
+ }
+
+ if (!chip->fcc_votable)
+ chip->fcc_votable = find_votable("FCC");
if (!chip->fcc_votable)
return -EINVAL;
- fcc_ua = get_fcc(pval.intval);
- if (fcc_ua < 0) {
- /* remove the vote if no step-based fcc is found */
- vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
- return 0;
- }
-
vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua);
pr_debug("%s = %d Step-FCC = %duA\n",
step_chg_config.prop_name, pval.intval, fcc_ua);
+update_time:
+ chip->step_last_update_time = ktime_get();
return 0;
+
+reschedule:
+ /* reschedule 1000uS after the remaining time */
+ return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
}
-#define STEP_CHG_HYSTERISIS_DELAY_US 5000000 /* 5 secs */
+static int handle_jeita(struct step_chg_info *chip)
+{
+ union power_supply_propval pval = {0, };
+ int rc = 0, fcc_ua = 0, fv_uv = 0;
+ u64 elapsed_us;
+
+ if (!chip->sw_jeita_enable) {
+ if (chip->fcc_votable)
+ vote(chip->fcc_votable, JEITA_VOTER, false, 0);
+ if (chip->fv_votable)
+ vote(chip->fv_votable, JEITA_VOTER, false, 0);
+ return 0;
+ }
+
+ elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time);
+ if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
+ goto reschedule;
+
+ rc = power_supply_get_property(chip->batt_psy,
+ jeita_fcc_config.psy_prop, &pval);
+ if (rc < 0) {
+ pr_err("Couldn't read %s property rc=%d\n",
+ step_chg_config.prop_name, rc);
+ return rc;
+ }
+
+ rc = get_val(jeita_fcc_config.fcc_cfg, jeita_fcc_config.hysteresis,
+ chip->jeita_fcc_index,
+ pval.intval,
+ &chip->jeita_fcc_index,
+ &fcc_ua);
+ if (rc < 0) {
+ /* remove the vote if no step-based fcc is found */
+ if (chip->fcc_votable)
+ vote(chip->fcc_votable, JEITA_VOTER, false, 0);
+ goto update_time;
+ }
+
+ if (!chip->fcc_votable)
+ chip->fcc_votable = find_votable("FCC");
+ if (!chip->fcc_votable)
+ /* changing FCC is a must */
+ return -EINVAL;
+
+ vote(chip->fcc_votable, JEITA_VOTER, true, fcc_ua);
+
+ rc = get_val(jeita_fv_config.fv_cfg, jeita_fv_config.hysteresis,
+ chip->jeita_fv_index,
+ pval.intval,
+ &chip->jeita_fv_index,
+ &fv_uv);
+ if (rc < 0) {
+ /* remove the vote if no step-based fcc is found */
+ if (chip->fv_votable)
+ vote(chip->fv_votable, JEITA_VOTER, false, 0);
+ goto update_time;
+ }
+
+ chip->fv_votable = find_votable("FV");
+ if (!chip->fv_votable)
+ goto update_time;
+
+ vote(chip->fv_votable, JEITA_VOTER, true, fv_uv);
+
+ pr_debug("%s = %d FCC = %duA FV = %duV\n",
+ step_chg_config.prop_name, pval.intval, fcc_ua, fv_uv);
+
+update_time:
+ chip->jeita_last_update_time = ktime_get();
+ return 0;
+
+reschedule:
+ /* reschedule 1000uS after the remaining time */
+ return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
+}
+
static void status_change_work(struct work_struct *work)
{
struct step_chg_info *chip = container_of(work,
struct step_chg_info, status_change_work.work);
int rc = 0;
- u64 elapsed_us;
-
- elapsed_us = ktime_us_delta(ktime_get(), chip->last_update_time);
- if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
- goto release_ws;
+ int reschedule_us;
+ int reschedule_jeita_work_us = 0;
+ int reschedule_step_work_us = 0;
if (!is_batt_available(chip))
- goto release_ws;
+ return;
+
+ /* skip elapsed_us debounce for handling battery temperature */
+ rc = handle_jeita(chip);
+ if (rc > 0)
+ reschedule_jeita_work_us = rc;
+ else if (rc < 0)
+ pr_err("Couldn't handle sw jeita rc = %d\n", rc);
rc = handle_step_chg_config(chip);
+ if (rc > 0)
+ reschedule_step_work_us = rc;
if (rc < 0)
- goto release_ws;
+ pr_err("Couldn't handle step rc = %d\n", rc);
- chip->last_update_time = ktime_get();
-
-release_ws:
- __pm_relax(chip->step_chg_ws);
+ reschedule_us = min(reschedule_jeita_work_us, reschedule_step_work_us);
+ if (reschedule_us == 0)
+ __pm_relax(chip->step_chg_ws);
+ else
+ schedule_delayed_work(&chip->status_change_work,
+ usecs_to_jiffies(reschedule_us));
}
static int step_chg_notifier_call(struct notifier_block *nb,
@@ -205,7 +405,7 @@
return 0;
}
-int qcom_step_chg_init(bool step_chg_enable)
+int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable)
{
int rc;
struct step_chg_info *chip;
@@ -226,12 +426,34 @@
}
chip->step_chg_enable = step_chg_enable;
+ chip->sw_jeita_enable = sw_jeita_enable;
+
+ chip->step_index = -EINVAL;
+ chip->jeita_fcc_index = -EINVAL;
+ chip->jeita_fv_index = -EINVAL;
if (step_chg_enable && (!step_chg_config.psy_prop ||
!step_chg_config.prop_name)) {
/* fail if step-chg configuration is invalid */
pr_err("Step-chg configuration not defined - fail\n");
- return -ENODATA;
+ rc = -ENODATA;
+ goto release_wakeup_source;
+ }
+
+ if (sw_jeita_enable && (!jeita_fcc_config.psy_prop ||
+ !jeita_fcc_config.prop_name)) {
+ /* fail if step-chg configuration is invalid */
+ pr_err("Jeita TEMP configuration not defined - fail\n");
+ rc = -ENODATA;
+ goto release_wakeup_source;
+ }
+
+ if (sw_jeita_enable && (!jeita_fv_config.psy_prop ||
+ !jeita_fv_config.prop_name)) {
+ /* fail if step-chg configuration is invalid */
+ pr_err("Jeita TEMP configuration not defined - fail\n");
+ rc = -ENODATA;
+ goto release_wakeup_source;
}
INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
diff --git a/drivers/power/supply/qcom/step-chg-jeita.h b/drivers/power/supply/qcom/step-chg-jeita.h
index 236877a..5bb2b99 100644
--- a/drivers/power/supply/qcom/step-chg-jeita.h
+++ b/drivers/power/supply/qcom/step-chg-jeita.h
@@ -12,6 +12,6 @@
#ifndef __STEP_CHG_H__
#define __STEP_CHG_H__
-int qcom_step_chg_init(bool step_chg_enable);
+int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable);
void qcom_step_chg_deinit(void);
#endif /* __STEP_CHG_H__ */
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index 8039346..11b4958 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -1119,10 +1119,21 @@
unsigned int rx_bytes = 0;
struct tty_port *tport;
int ret;
+ unsigned int geni_status;
+
+ geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+ /* Possible stop rx is called */
+ if (!(geni_status & S_GENI_CMD_ACTIVE))
+ return 0;
geni_se_rx_dma_unprep(msm_port->wrapper_dev, msm_port->rx_dma,
DMA_RX_BUF_SIZE);
rx_bytes = geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN);
+ if (unlikely(!msm_port->rx_buf || !rx_bytes)) {
+ IPC_LOG_MSG(msm_port->ipc_log_rx, "%s: Rx_buf %pK Size %d\n",
+ __func__, msm_port->rx_buf, rx_bytes);
+ return 0;
+ }
tport = &uport->state->port;
ret = tty_insert_flip_string(tport, (unsigned char *)(msm_port->rx_buf),
@@ -1313,13 +1324,13 @@
wait_for_transfers_inflight(uport);
}
+ disable_irq(uport->irq);
+ free_irq(uport->irq, msm_port);
spin_lock_irqsave(&uport->lock, flags);
msm_geni_serial_stop_tx(uport);
msm_geni_serial_stop_rx(uport);
spin_unlock_irqrestore(&uport->lock, flags);
- disable_irq(uport->irq);
- free_irq(uport->irq, msm_port);
if (uart_console(uport)) {
se_geni_resources_off(&msm_port->serial_rsc);
} else {
diff --git a/include/linux/qcom_tspp.h b/include/linux/qcom_tspp.h
index 1b34c38..7a9e569 100644
--- a/include/linux/qcom_tspp.h
+++ b/include/linux/qcom_tspp.h
@@ -16,6 +16,7 @@
struct tspp_data_descriptor {
void *virt_base; /* logical address of the actual data */
phys_addr_t phys_base; /* physical address of the actual data */
+ dma_addr_t dma_base; /* DMA address of the actual data */
u32 size; /* size of buffer in bytes */
int id; /* unique identifier */
void *user; /* user-defined data */
@@ -75,9 +76,17 @@
TSIF_TTS_LPASS_TIMER /* Time stamps from AV/Qtimer Timer */
};
+struct tspp_ion_dma_buf_info {
+ struct dma_buf *dbuf;
+ struct dma_buf_attachment *attach;
+ struct sg_table *table;
+ bool smmu_map;
+ dma_addr_t dma_map_base;
+};
+
typedef void (tspp_notifier)(int channel_id, void *user);
typedef void* (tspp_allocator)(int channel_id, u32 size,
- phys_addr_t *phys_base, void *user);
+ phys_addr_t *phys_base, dma_addr_t *dma_base, void *user);
typedef void (tspp_memfree)(int channel_id, u32 size,
void *virt_base, phys_addr_t phys_base, void *user);
@@ -105,4 +114,9 @@
int tspp_get_lpass_time_counter(u32 dev, enum tspp_source source,
u64 *lpass_time_counter);
+int tspp_attach_ion_dma_buff(u32 dev,
+ struct tspp_ion_dma_buf_info *ion_dma_buf);
+
+int tspp_detach_ion_dma_buff(u32 dev,
+ struct tspp_ion_dma_buf_info *ion_dma_buf);
#endif /* _MSM_TSPP_H_ */