Merge "defconfig: enable NETFILTER_XT_MATCH_QUOTA2_LOG for data metering"
diff --git a/arch/arm64/configs/vendor/bengal-perf_defconfig b/arch/arm64/configs/vendor/bengal-perf_defconfig
index 395dc68..dea94f3 100644
--- a/arch/arm64/configs/vendor/bengal-perf_defconfig
+++ b/arch/arm64/configs/vendor/bengal-perf_defconfig
@@ -422,6 +422,7 @@
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_RPM=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=y
+CONFIG_MSM_RPM_SMD=y
CONFIG_QCOM_COMMAND_DB=y
CONFIG_QCOM_CPUSS_DUMP=y
CONFIG_QCOM_RUN_QUEUE_STATS=y
diff --git a/arch/arm64/configs/vendor/bengal_defconfig b/arch/arm64/configs/vendor/bengal_defconfig
index fa34788..6cd50a0 100644
--- a/arch/arm64/configs/vendor/bengal_defconfig
+++ b/arch/arm64/configs/vendor/bengal_defconfig
@@ -441,6 +441,7 @@
CONFIG_RPMSG_CHAR=y
CONFIG_RPMSG_QCOM_GLINK_RPM=y
CONFIG_RPMSG_QCOM_GLINK_SMEM=y
+CONFIG_MSM_RPM_SMD=y
CONFIG_QCOM_COMMAND_DB=y
CONFIG_QCOM_CPUSS_DUMP=y
CONFIG_QCOM_RUN_QUEUE_STATS=y
@@ -473,6 +474,7 @@
CONFIG_PANIC_ON_GLADIATOR_ERROR=y
CONFIG_QCOM_WATCHDOG_V2=y
CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_QCOM_BUS_SCALING=y
CONFIG_QCOM_GLINK=y
CONFIG_QCOM_GLINK_PKT=y
CONFIG_QCOM_SMP2P_SLEEPSTATE=y
diff --git a/arch/arm64/configs/vendor/lito_defconfig b/arch/arm64/configs/vendor/lito_defconfig
index 9e07f4f5..e722335 100644
--- a/arch/arm64/configs/vendor/lito_defconfig
+++ b/arch/arm64/configs/vendor/lito_defconfig
@@ -670,6 +670,7 @@
CONFIG_PRINTK_TIME=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_CONSOLE_UNHASHED_POINTERS=y
+CONFIG_DEBUG_MODULE_LOAD_INFO=y
CONFIG_DEBUG_INFO=y
CONFIG_PAGE_OWNER=y
CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
diff --git a/drivers/bus/mhi/core/mhi_init.c b/drivers/bus/mhi/core/mhi_init.c
index 16ac408..35a084b 100644
--- a/drivers/bus/mhi/core/mhi_init.c
+++ b/drivers/bus/mhi/core/mhi_init.c
@@ -1613,7 +1613,6 @@ static int mhi_driver_probe(struct device *dev)
struct mhi_event *mhi_event;
struct mhi_chan *ul_chan = mhi_dev->ul_chan;
struct mhi_chan *dl_chan = mhi_dev->dl_chan;
- bool auto_start = false;
int ret;
/* bring device out of lpm */
@@ -1632,7 +1631,11 @@ static int mhi_driver_probe(struct device *dev)
ul_chan->xfer_cb = mhi_drv->ul_xfer_cb;
mhi_dev->status_cb = mhi_drv->status_cb;
- auto_start = ul_chan->auto_start;
+ if (ul_chan->auto_start) {
+ ret = mhi_prepare_channel(mhi_cntrl, ul_chan);
+ if (ret)
+ goto exit_probe;
+ }
}
if (dl_chan) {
@@ -1656,15 +1659,22 @@ static int mhi_driver_probe(struct device *dev)
/* ul & dl uses same status cb */
mhi_dev->status_cb = mhi_drv->status_cb;
- auto_start = (auto_start || dl_chan->auto_start);
}
ret = mhi_drv->probe(mhi_dev, mhi_dev->id);
+ if (ret)
+ goto exit_probe;
- if (!ret && auto_start)
- mhi_prepare_for_transfer(mhi_dev);
+ if (dl_chan && dl_chan->auto_start)
+ mhi_prepare_channel(mhi_cntrl, dl_chan);
+
+ mhi_device_put(mhi_dev, MHI_VOTE_DEVICE);
+
+ return ret;
exit_probe:
+ mhi_unprepare_from_transfer(mhi_dev);
+
mhi_device_put(mhi_dev, MHI_VOTE_DEVICE);
return ret;
diff --git a/drivers/bus/mhi/core/mhi_internal.h b/drivers/bus/mhi/core/mhi_internal.h
index 69129b6..bb2379e 100644
--- a/drivers/bus/mhi/core/mhi_internal.h
+++ b/drivers/bus/mhi/core/mhi_internal.h
@@ -878,6 +878,8 @@ void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl);
int mhi_dtr_init(void);
void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info);
+int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
+ struct mhi_chan *mhi_chan);
/* isr handlers */
irqreturn_t mhi_msi_handlr(int irq_number, void *dev);
diff --git a/drivers/bus/mhi/core/mhi_main.c b/drivers/bus/mhi/core/mhi_main.c
index 8e1e2fd..cd77709 100644
--- a/drivers/bus/mhi/core/mhi_main.c
+++ b/drivers/bus/mhi/core/mhi_main.c
@@ -1569,7 +1569,8 @@ irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev)
state = mhi_get_mhi_state(mhi_cntrl);
ee = mhi_cntrl->ee;
mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl);
- MHI_LOG("device ee:%s dev_state:%s\n",
+ MHI_LOG("local ee: %s device ee:%s dev_state:%s\n",
+ TO_MHI_EXEC_STR(ee),
TO_MHI_EXEC_STR(mhi_cntrl->ee),
TO_MHI_STATE_STR(state));
}
@@ -1582,7 +1583,7 @@ irqreturn_t mhi_intvec_threaded_handlr(int irq_number, void *dev)
write_unlock_irq(&mhi_cntrl->pm_lock);
/* if device in rddm don't bother processing sys error */
- if (mhi_cntrl->ee == MHI_EE_RDDM) {
+ if (mhi_cntrl->ee == MHI_EE_RDDM && ee != MHI_EE_DISABLE_TRANSITION) {
if (mhi_cntrl->ee != ee) {
mhi_cntrl->status_cb(mhi_cntrl, mhi_cntrl->priv_data,
MHI_CB_EE_RDDM);
@@ -1688,8 +1689,8 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl,
return 0;
}
-static int __mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
- struct mhi_chan *mhi_chan)
+int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
+ struct mhi_chan *mhi_chan)
{
int ret = 0;
@@ -2076,7 +2077,7 @@ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev)
if (!mhi_chan)
continue;
- ret = __mhi_prepare_channel(mhi_cntrl, mhi_chan);
+ ret = mhi_prepare_channel(mhi_cntrl, mhi_chan);
if (ret) {
MHI_ERR("Error moving chan %s,%d to START state\n",
mhi_chan->name, mhi_chan->chan);
diff --git a/drivers/bus/mhi/devices/mhi_satellite.c b/drivers/bus/mhi/devices/mhi_satellite.c
index 9fff109..d9de31a 100644
--- a/drivers/bus/mhi/devices/mhi_satellite.c
+++ b/drivers/bus/mhi/devices/mhi_satellite.c
@@ -235,14 +235,15 @@ enum mhi_sat_state {
SAT_DISCONNECTED, /* rpmsg link is down */
SAT_FATAL_DETECT, /* device is down as fatal error was detected early */
SAT_ERROR, /* device is down after error or graceful shutdown */
- SAT_DISABLED, /* set if rpmsg link goes down after device is down */
+ SAT_DISABLED, /* no further processing: wait for device removal */
};
#define MHI_SAT_ACTIVE(cntrl) (cntrl->state == SAT_RUNNING)
-#define MHI_SAT_FATAL_DETECT(cntrl) (cntrl->state == SAT_FATAL_DETECT)
+#define MHI_SAT_IN_ERROR_STATE(cntrl) (cntrl->state >= SAT_FATAL_DETECT)
#define MHI_SAT_ALLOW_CONNECTION(cntrl) (cntrl->state == SAT_READY || \
cntrl->state == SAT_DISCONNECTED)
-#define MHI_SAT_IN_ERROR_STATE(cntrl) (cntrl->state >= SAT_FATAL_DETECT)
+#define MHI_SAT_ALLOW_SYS_ERR(cntrl) (cntrl->state == SAT_RUNNING || \
+ cntrl->state == SAT_FATAL_DETECT)
struct mhi_sat_cntrl {
struct list_head node;
@@ -940,10 +941,15 @@ static void mhi_sat_dev_status_cb(struct mhi_device *mhi_dev,
MHI_SAT_LOG("Device fatal error detected\n");
spin_lock_irqsave(&sat_cntrl->state_lock, flags);
- if (MHI_SAT_ACTIVE(sat_cntrl))
+ if (MHI_SAT_ACTIVE(sat_cntrl)) {
sat_cntrl->error_cookie = async_schedule(mhi_sat_error_worker,
sat_cntrl);
- sat_cntrl->state = SAT_FATAL_DETECT;
+ sat_cntrl->state = SAT_FATAL_DETECT;
+ } else {
+ /* rpmsg link down or HELLO not sent or an error occurred */
+ sat_cntrl->state = SAT_DISABLED;
+ }
+
spin_unlock_irqrestore(&sat_cntrl->state_lock, flags);
}
@@ -968,7 +974,7 @@ static void mhi_sat_dev_remove(struct mhi_device *mhi_dev)
/* send sys_err if first device is removed */
spin_lock_irq(&sat_cntrl->state_lock);
- if (MHI_SAT_ACTIVE(sat_cntrl) || MHI_SAT_FATAL_DETECT(sat_cntrl))
+ if (MHI_SAT_ALLOW_SYS_ERR(sat_cntrl))
send_sys_err = true;
sat_cntrl->state = SAT_ERROR;
spin_unlock_irq(&sat_cntrl->state_lock);
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index c13f049..c1a3b89 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -263,6 +263,7 @@ struct smq_invoke_ctx {
struct fastrpc_buf *lbuf;
size_t used;
struct fastrpc_file *fl;
+ uint32_t handle;
uint32_t sc;
struct overlap *overs;
struct overlap **overps;
@@ -365,6 +366,8 @@ struct fastrpc_apps {
bool legacy_remote_heap;
/* Unique job id for each message */
uint64_t jobid[NUM_CHANNELS];
+ struct wakeup_source *wake_source;
+ unsigned int wake_count;
};
struct fastrpc_mmap {
@@ -446,6 +449,8 @@ struct fastrpc_file {
/* Identifies the device (MINOR_NUM_DEV / MINOR_NUM_SECURE_DEV) */
int dev_minor;
char *debug_buf;
+ /* Flag to enable PM wake/relax voting for every remote invoke */
+ int wake_enable;
};
static struct fastrpc_apps gfa;
@@ -1319,6 +1324,7 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel,
goto bail;
}
ctx->crc = (uint32_t *)invokefd->crc;
+ ctx->handle = invoke->handle;
ctx->sc = invoke->sc;
if (bufs) {
VERIFY(err, 0 == context_build_overlap(ctx));
@@ -2025,7 +2031,36 @@ static void fastrpc_init(struct fastrpc_apps *me)
me->channel[CDSP_DOMAIN_ID].secure = NON_SECURE_CHANNEL;
}
-static int fastrpc_release_current_dsp_process(struct fastrpc_file *fl);
+static inline void fastrpc_pm_awake(int fl_wake_enable, int *wake_enable)
+{
+ struct fastrpc_apps *me = &gfa;
+
+ if (!fl_wake_enable)
+ return;
+
+ spin_lock(&me->hlock);
+ if (!me->wake_count)
+ __pm_stay_awake(me->wake_source);
+ me->wake_count++;
+ spin_unlock(&me->hlock);
+ *wake_enable = 1;
+}
+
+static inline void fastrpc_pm_relax(int *wake_enable)
+{
+ struct fastrpc_apps *me = &gfa;
+
+ if (!(*wake_enable))
+ return;
+
+ spin_lock(&me->hlock);
+ if (me->wake_count)
+ me->wake_count--;
+ if (!me->wake_count)
+ __pm_relax(me->wake_source);
+ spin_unlock(&me->hlock);
+ *wake_enable = 0;
+}
static inline int fastrpc_wait_for_response(struct smq_invoke_ctx *ctx,
uint32_t kernel)
@@ -2083,8 +2118,8 @@ static void fastrpc_wait_for_completion(struct smq_invoke_ctx *ctx,
if (!err) {
ctx->isWorkDone = true;
} else if (!ctx->isWorkDone) {
- pr_info("poll timeout ctxid 0x%llx sc 0x%x\n",
- ctx->ctxid, ctx->sc);
+ pr_info("adsprpc: %s: %s: poll timeout for handle 0x%x, sc 0x%x\n",
+ __func__, current->comm, ctx->handle, ctx->sc);
interrupted = fastrpc_wait_for_response(ctx,
kernel);
*pInterrupted = interrupted;
@@ -2102,8 +2137,9 @@ static void fastrpc_wait_for_completion(struct smq_invoke_ctx *ctx,
break;
default:
- pr_err("adsprpc: unsupported response flags 0x%x\n",
- ctx->rspFlags);
+ *pInterrupted = -EBADR;
+ pr_err("Error: adsprpc: %s: unsupported response flags 0x%x for handle 0x%x, sc 0x%x\n",
+ current->comm, ctx->rspFlags, ctx->handle, ctx->sc);
return;
} /* end of switch */
} /* end of while loop */
@@ -2135,10 +2171,11 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
struct fastrpc_ioctl_invoke *invoke = &inv->inv;
int cid = fl->cid;
int interrupted = 0;
- int err = 0;
+ int err = 0, wake_enable = 0;
struct timespec invoket = {0};
int64_t *perf_counter = NULL;
+ fastrpc_pm_awake(fl->wake_enable, &wake_enable);
if (fl->profile) {
perf_counter = getperfcounter(fl, PERF_COUNT);
getnstimeofday(&invoket);
@@ -2158,7 +2195,7 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
VERIFY(err, fl->cid >= 0 && fl->cid < NUM_CHANNELS && fl->sctx != NULL);
if (err) {
- pr_err("adsprpc: ERROR: %s: user application %s domain is not set\n",
+ pr_err("adsprpc: ERROR: %s: kernel session not initialized yet for %s\n",
__func__, current->comm);
err = -EBADR;
goto bail;
@@ -2204,15 +2241,17 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
if (err)
goto bail;
wait:
+ fastrpc_pm_relax(&wake_enable);
fastrpc_wait_for_completion(ctx, &interrupted, kernel);
+ fastrpc_pm_awake(fl->wake_enable, &wake_enable);
VERIFY(err, 0 == (err = interrupted));
if (err)
goto bail;
- VERIFY(err, ctx->isWorkDone);
- if (err) {
- pr_err("adsprpc: ctx work done failed, sc 0x%x handle 0x%x\n",
- ctx->sc, invoke->handle);
+ if (!ctx->isWorkDone) {
+ err = -EPROTO;
+ pr_err("Error: adsprpc: %s: %s: WorkDone state is invalid for handle 0x%x, sc 0x%x\n",
+ __func__, current->comm, invoke->handle, ctx->sc);
goto bail;
}
@@ -2240,6 +2279,7 @@ static int fastrpc_internal_invoke(struct fastrpc_file *fl, uint32_t mode,
if (fl->profile && !interrupted)
fastrpc_update_invoke_count(invoke->handle, perf_counter,
&invoket);
+ fastrpc_pm_relax(&wake_enable);
return err;
}
@@ -3204,9 +3244,12 @@ static int fastrpc_session_alloc_locked(struct fastrpc_channel_ctx *chan,
break;
}
}
- VERIFY(err, idx < chan->sesscount);
- if (err)
+ if (idx >= chan->sesscount) {
+ err = -EUSERS;
+ pr_err("adsprpc: ERROR %d: %s: max concurrent sessions limit (%d) already reached on %s\n",
+ err, __func__, chan->sesscount, chan->subsys);
goto bail;
+ }
chan->session[idx].smmu.faults = 0;
} else {
VERIFY(err, me->dev != NULL);
@@ -3710,7 +3753,7 @@ static int fastrpc_channel_open(struct fastrpc_file *fl)
VERIFY(err, fl && fl->sctx && fl->cid >= 0 && fl->cid < NUM_CHANNELS);
if (err) {
- pr_err("adsprpc: ERROR: %s: user application %s domain is not set\n",
+ pr_err("adsprpc: ERROR: %s: kernel session not initialized yet for %s\n",
__func__, current->comm);
err = -EBADR;
return err;
@@ -3852,8 +3895,8 @@ static int fastrpc_get_info(struct fastrpc_file *fl, uint32_t *info)
fl->cid = cid;
fl->ssrcount = fl->apps->channel[cid].ssrcount;
mutex_lock(&fl->apps->channel[cid].smd_mutex);
- VERIFY(err, 0 == (err = fastrpc_session_alloc_locked(
- &fl->apps->channel[cid], 0, &fl->sctx)));
+ err = fastrpc_session_alloc_locked(&fl->apps->channel[cid],
+ 0, &fl->sctx);
mutex_unlock(&fl->apps->channel[cid].smd_mutex);
if (err)
goto bail;
@@ -3899,8 +3942,11 @@ static int fastrpc_internal_control(struct fastrpc_file *fl,
case FASTRPC_CONTROL_KALLOC:
cp->kalloc.kalloc_support = 1;
break;
+ case FASTRPC_CONTROL_WAKELOCK:
+ fl->wake_enable = cp->wp.enable;
+ break;
default:
- err = -ENOTTY;
+ err = -EBADRQC;
break;
}
bail:
@@ -4014,6 +4060,7 @@ static int fastrpc_control(struct fastrpc_ioctl_control *cp,
bail:
return err;
}
+
static int fastrpc_get_dsp_info(struct fastrpc_ioctl_dsp_capabilities *dsp_cap,
void *param, struct fastrpc_file *fl)
{
@@ -4789,11 +4836,19 @@ static int __init fastrpc_device_init(void)
err = register_rpmsg_driver(&fastrpc_rpmsg_client);
if (err) {
- pr_err("adsprpc: register_rpmsg_driver: failed with err %d\n",
- err);
+ pr_err("adsprpc: %s: register_rpmsg_driver failed with err %d\n",
+ __func__, err);
goto device_create_bail;
}
me->rpmsg_register = 1;
+
+ me->wake_source = wakeup_source_register("adsprpc");
+ VERIFY(err, !IS_ERR_OR_NULL(me->wake_source));
+ if (err) {
+ pr_err("adsprpc: Error: %s: wakeup_source_register failed with err %d\n",
+ __func__, PTR_ERR(me->wake_source));
+ goto device_create_bail;
+ }
return 0;
device_create_bail:
for (i = 0; i < NUM_CHANNELS; i++) {
@@ -4844,6 +4899,8 @@ static void __exit fastrpc_device_exit(void)
unregister_chrdev_region(me->dev_no, NUM_CHANNELS);
if (me->rpmsg_register == 1)
unregister_rpmsg_driver(&fastrpc_rpmsg_client);
+ if (me->wake_source)
+ wakeup_source_unregister(me->wake_source);
debugfs_remove_recursive(debugfs_root);
}
diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
index 571f585..c1e5af9 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -234,23 +234,32 @@ struct fastrpc_ioctl_perf { /* kernel performance data */
uintptr_t keys;
};
-#define FASTRPC_CONTROL_LATENCY (1)
+enum fastrpc_control_type {
+ FASTRPC_CONTROL_LATENCY = 1,
+ FASTRPC_CONTROL_SMMU = 2,
+ FASTRPC_CONTROL_KALLOC = 3,
+ FASTRPC_CONTROL_WAKELOCK = 4,
+};
+
struct fastrpc_ctrl_latency {
uint32_t enable; /* latency control enable */
uint32_t latency; /* latency request in us */
};
-#define FASTRPC_CONTROL_KALLOC (3)
struct fastrpc_ctrl_kalloc {
uint32_t kalloc_support; /* Remote memory allocation from kernel */
};
-/* FASTRPC_CONTROL value 2 is reserved in user space */
+struct fastrpc_ctrl_wakelock {
+ uint32_t enable; /* wakelock control enable */
+};
+
struct fastrpc_ioctl_control {
uint32_t req;
union {
struct fastrpc_ctrl_latency lp;
struct fastrpc_ctrl_kalloc kalloc;
+ struct fastrpc_ctrl_wakelock wp;
};
};
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 950d04c..9cfe1c1 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1044,6 +1044,7 @@ static void diag_init_apps_feature(void)
SET_APPS_FEATURE(driver, F_DIAG_EVENT_REPORT);
SET_APPS_FEATURE(driver, F_DIAG_HW_ACCELERATION);
+ SET_APPS_FEATURE(driver, F_DIAG_MULTI_SIM_MASK);
}
void diag_send_error_rsp(unsigned char *buf, int len,
diff --git a/drivers/clk/qcom/camcc-lito.c b/drivers/clk/qcom/camcc-lito.c
index 20c3295..e7900c8 100644
--- a/drivers/clk/qcom/camcc-lito.c
+++ b/drivers/clk/qcom/camcc-lito.c
@@ -324,7 +324,7 @@ static struct clk_alpha_pll_postdiv cam_cc_pll1_out_even = {
},
};
-static const struct alpha_pll_config cam_cc_pll2_config = {
+static struct alpha_pll_config cam_cc_pll2_config = {
.l = 0x32,
.cal_l = 0x32,
.alpha = 0x0,
@@ -2319,6 +2319,7 @@ static const struct qcom_cc_desc cam_cc_lito_desc = {
static const struct of_device_id cam_cc_lito_match_table[] = {
{ .compatible = "qcom,lito-camcc" },
+ { .compatible = "qcom,lito-camcc-v2" },
{ }
};
MODULE_DEVICE_TABLE(of, cam_cc_lito_match_table);
@@ -2361,6 +2362,10 @@ static int cam_cc_lito_probe(struct platform_device *pdev)
clk_lucid_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config);
clk_lucid_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config);
+
+ if (of_device_is_compatible(pdev->dev.of_node, "qcom,lito-camcc-v2"))
+ cam_cc_pll2_config.test_ctl_val = 0x00000000;
+
clk_zonda_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config);
clk_lucid_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config);
clk_lucid_pll_configure(&cam_cc_pll4, regmap, &cam_cc_pll4_config);
diff --git a/drivers/clk/qcom/clk-voter.c b/drivers/clk/qcom/clk-voter.c
index 1ffc5e5..b8f585f 100644
--- a/drivers/clk/qcom/clk-voter.c
+++ b/drivers/clk/qcom/clk-voter.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017, 2019, The Linux Foundation. All rights reserved.
*/
#include <linux/clk.h>
@@ -126,6 +126,16 @@ static unsigned long voter_clk_recalc_rate(struct clk_hw *hw,
return v->rate;
}
+int voter_clk_handoff(struct clk_hw *hw)
+{
+ struct clk_voter *v = to_clk_voter(hw);
+
+ v->enabled = true;
+
+ return 0;
+}
+EXPORT_SYMBOL(voter_clk_handoff);
+
const struct clk_ops clk_ops_voter = {
.prepare = voter_clk_prepare,
.unprepare = voter_clk_unprepare,
diff --git a/drivers/clk/qcom/clk-voter.h b/drivers/clk/qcom/clk-voter.h
index b9a36f6..57333bb 100644
--- a/drivers/clk/qcom/clk-voter.h
+++ b/drivers/clk/qcom/clk-voter.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017, 2019, The Linux Foundation. All rights reserved.
*/
#ifndef __QCOM_CLK_VOTER_H__
@@ -27,6 +27,7 @@ extern const struct clk_ops clk_ops_voter;
.hw.init = &(struct clk_init_data){ \
.ops = &clk_ops_voter, \
.name = #clk_name, \
+ .flags = CLK_ENABLE_HAND_OFF, \
.parent_names = (const char *[]){ #_parent_name }, \
.num_parents = 1, \
}, \
@@ -38,4 +39,6 @@ extern const struct clk_ops clk_ops_voter;
#define DEFINE_CLK_BRANCH_VOTER(clk_name, _parent_name) \
__DEFINE_CLK_VOTER(clk_name, _parent_name, 1000, 1)
+int voter_clk_handoff(struct clk_hw *hw);
+
#endif
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index a9bcfbb..d00c6f5 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -3227,12 +3227,17 @@ static void _aead_aes_fb_stage1_ahash_complete(
unsigned char *tmp;
tmp = kmalloc(ctx->authsize, GFP_KERNEL);
+ if (!tmp) {
+ err = -ENOMEM;
+ goto ret;
+ }
scatterwalk_map_and_copy(tmp, rctx->fb_aes_src,
req->cryptlen - ctx->authsize, ctx->authsize, 0);
if (memcmp(rctx->fb_ahash_digest, tmp, ctx->authsize) != 0)
err = -EBADMSG;
kfree(tmp);
}
+ret:
if (err)
_qcrypto_aead_aes_192_fb_a_cb(rctx, err);
else {
@@ -3359,6 +3364,10 @@ static int _qcrypto_aead_aes_192_fallback(struct aead_request *req,
unsigned char *tmp;
tmp = kmalloc(ctx->authsize, GFP_KERNEL);
+ if (!tmp) {
+ rc = -ENOMEM;
+ goto ret;
+ }
/* compare icv */
scatterwalk_map_and_copy(tmp,
src, req->cryptlen - ctx->authsize,
diff --git a/drivers/gpu/msm/adreno_a6xx_gmu.c b/drivers/gpu/msm/adreno_a6xx_gmu.c
index 7495487..b6c699f 100644
--- a/drivers/gpu/msm/adreno_a6xx_gmu.c
+++ b/drivers/gpu/msm/adreno_a6xx_gmu.c
@@ -346,7 +346,7 @@ static int a6xx_gmu_start(struct kgsl_device *device)
u32 mask = 0x000001FF;
/* Check for 0xBABEFACE on legacy targets */
- if (gmu->ver.core <= 0x20010003) {
+ if (gmu->ver.core <= 0x20010004) {
val = 0xBABEFACE;
mask = 0xFFFFFFFF;
}
@@ -1156,31 +1156,11 @@ static int a6xx_gmu_load_firmware(struct kgsl_device *device)
#define A6XX_VBIF_XIN_HALT_CTRL1_ACKS (BIT(0) | BIT(1) | BIT(2) | BIT(3))
-static void a6xx_isense_disable(struct kgsl_device *device)
-{
- unsigned int val;
- const struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-
- if (!ADRENO_FEATURE(adreno_dev, ADRENO_LM) ||
- !test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag))
- return;
-
- gmu_core_regread(device, A6XX_GPU_CS_ENABLE_REG, &val);
- if (val) {
- gmu_core_regwrite(device, A6XX_GPU_CS_ENABLE_REG, 0);
- gmu_core_regwrite(device, A6XX_GMU_ISENSE_CTRL, 0);
- }
-}
-
static int a6xx_gmu_suspend(struct kgsl_device *device)
{
int ret = 0;
struct gmu_device *gmu = KGSL_GMU_DEVICE(device);
- /* do it only if LM feature is enabled */
- /* Disable ISENSE if it's on */
- a6xx_isense_disable(device);
-
/* If SPTP_RAC is on, turn off SPTP_RAC HS */
a6xx_gmu_sptprac_disable(ADRENO_DEVICE(device));
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index fe0a4b5..41047a0 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -3624,12 +3624,16 @@ long kgsl_ioctl_sparse_phys_alloc(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
struct kgsl_process_private *process = dev_priv->process_priv;
+ struct kgsl_device *device = dev_priv->device;
struct kgsl_sparse_phys_alloc *param = data;
struct kgsl_mem_entry *entry;
uint64_t flags;
int ret;
int id;
+ if (!(device->flags & KGSL_FLAG_SPARSE))
+ return -ENOTSUPP;
+
ret = _sparse_alloc_param_sanity_check(param->size, param->pagesize);
if (ret)
return ret;
@@ -3713,9 +3717,13 @@ long kgsl_ioctl_sparse_phys_free(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
struct kgsl_process_private *process = dev_priv->process_priv;
+ struct kgsl_device *device = dev_priv->device;
struct kgsl_sparse_phys_free *param = data;
struct kgsl_mem_entry *entry;
+ if (!(device->flags & KGSL_FLAG_SPARSE))
+ return -ENOTSUPP;
+
entry = kgsl_sharedmem_find_id_flags(process, param->id,
KGSL_MEMFLAGS_SPARSE_PHYS);
if (entry == NULL)
@@ -3745,10 +3753,14 @@ long kgsl_ioctl_sparse_virt_alloc(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
struct kgsl_process_private *private = dev_priv->process_priv;
+ struct kgsl_device *device = dev_priv->device;
struct kgsl_sparse_virt_alloc *param = data;
struct kgsl_mem_entry *entry;
int ret;
+ if (!(device->flags & KGSL_FLAG_SPARSE))
+ return -ENOTSUPP;
+
ret = _sparse_alloc_param_sanity_check(param->size, param->pagesize);
if (ret)
return ret;
@@ -3789,9 +3801,13 @@ long kgsl_ioctl_sparse_virt_free(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
struct kgsl_process_private *process = dev_priv->process_priv;
+ struct kgsl_device *device = dev_priv->device;
struct kgsl_sparse_virt_free *param = data;
struct kgsl_mem_entry *entry = NULL;
+ if (!(device->flags & KGSL_FLAG_SPARSE))
+ return -ENOTSUPP;
+
entry = kgsl_sharedmem_find_id_flags(process, param->id,
KGSL_MEMFLAGS_SPARSE_VIRT);
if (entry == NULL)
@@ -4138,6 +4154,7 @@ long kgsl_ioctl_sparse_bind(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
struct kgsl_process_private *private = dev_priv->process_priv;
+ struct kgsl_device *device = dev_priv->device;
struct kgsl_sparse_bind *param = data;
struct kgsl_sparse_binding_object obj;
struct kgsl_mem_entry *virt_entry;
@@ -4146,6 +4163,9 @@ long kgsl_ioctl_sparse_bind(struct kgsl_device_private *dev_priv,
int ret = 0;
int i = 0;
+ if (!(device->flags & KGSL_FLAG_SPARSE))
+ return -ENOTSUPP;
+
ptr = (void __user *) (uintptr_t) param->list;
if (param->size > sizeof(struct kgsl_sparse_binding_object) ||
@@ -4201,6 +4221,9 @@ long kgsl_ioctl_gpu_sparse_command(struct kgsl_device_private *dev_priv,
long result;
unsigned int i = 0;
+ if (!(device->flags & KGSL_FLAG_SPARSE))
+ return -ENOTSUPP;
+
/* Make sure sparse and syncpoint count isn't too big */
if (param->numsparse > KGSL_MAX_SPARSE ||
param->numsyncs > KGSL_MAX_SYNCPOINTS)
@@ -5011,6 +5034,9 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
if (status)
return status;
+ /* Disable the sparse ioctl invocation as they are not used */
+ device->flags &= ~KGSL_FLAG_SPARSE;
+
kgsl_device_debugfs_init(device);
status = kgsl_pwrctrl_init(device);
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index e1979cd..8813993 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -50,6 +50,7 @@ enum kgsl_event_results {
};
#define KGSL_FLAG_WAKE_ON_TOUCH BIT(0)
+#define KGSL_FLAG_SPARSE BIT(1)
/*
* "list" of event types for ftrace symbolic magic
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index d449e60..3ab671d 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -115,6 +115,7 @@ struct geni_i2c_dev {
struct msm_gpi_dma_async_tx_cb_param rx_cb;
enum i2c_se_mode se_mode;
bool cmd_done;
+ bool is_shared;
};
struct geni_i2c_err_log {
@@ -420,15 +421,6 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
int i, ret = 0, timeout = 0;
- ret = pinctrl_select_state(gi2c->i2c_rsc.geni_pinctrl,
- gi2c->i2c_rsc.geni_gpio_active);
- if (ret) {
- GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
- "%s: Error %d pinctrl_select_state active\n",
- __func__, ret);
- return ret;
- }
-
if (!gi2c->tx_c) {
gi2c->tx_c = dma_request_slave_channel(gi2c->dev, "tx");
if (!gi2c->tx_c) {
@@ -647,8 +639,6 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
geni_i2c_gsi_xfer_out:
if (!ret && gi2c->err)
ret = gi2c->err;
- pinctrl_select_state(gi2c->i2c_rsc.geni_pinctrl,
- gi2c->i2c_rsc.geni_gpio_sleep);
return ret;
}
@@ -907,6 +897,11 @@ static int geni_i2c_probe(struct platform_device *pdev)
return ret;
}
+ if (of_property_read_bool(pdev->dev.of_node, "qcom,shared")) {
+ gi2c->is_shared = true;
+ dev_info(&pdev->dev, "Multi-EE usecase\n");
+ }
+
if (of_property_read_u32(pdev->dev.of_node, "qcom,clk-freq-out",
&gi2c->i2c_rsc.clk_freq_out)) {
dev_info(&pdev->dev,
@@ -984,12 +979,14 @@ static int geni_i2c_runtime_suspend(struct device *dev)
{
struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
- if (gi2c->se_mode == FIFO_SE_DMA) {
+ if (gi2c->se_mode == FIFO_SE_DMA)
disable_irq(gi2c->irq);
- se_geni_resources_off(&gi2c->i2c_rsc);
- } else {
- /* GPIO is set to sleep state already. So just clocks off */
+
+ if (gi2c->is_shared) {
+ /* Do not unconfigure GPIOs if shared se */
se_geni_clks_off(&gi2c->i2c_rsc);
+ } else {
+ se_geni_resources_off(&gi2c->i2c_rsc);
}
return 0;
}
@@ -1006,10 +1003,7 @@ static int geni_i2c_runtime_resume(struct device *dev)
gi2c->ipcl = ipc_log_context_create(2, ipc_name, 0);
}
- if (gi2c->se_mode != GSI_ONLY)
- ret = se_geni_resources_on(&gi2c->i2c_rsc);
- else
- ret = se_geni_clks_on(&gi2c->i2c_rsc);
+ ret = se_geni_resources_on(&gi2c->i2c_rsc);
if (ret)
return ret;
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 5299eea..11f7366 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -856,6 +856,17 @@
To compile this driver as a module, choose M here: the
module will be called zylonite-wm97xx.
+config SECURE_TOUCH_SYNAPTICS_DSX
+ bool "Secure Touch"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_I2C
+ help
+ Say Y here to enable Secure Touch in supported driver.
+
+ If unsure, say N.
+
+ To compile the supported driver with Secure Touch enabled,
+ choose M here.
+
config TOUCHSCREEN_USB_COMPOSITE
tristate "USB Touchscreen Driver"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
index e4ff2ea..4e2dc13 100755
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
@@ -45,6 +45,16 @@
#ifdef KERNEL_ABOVE_2_6_38
#include <linux/input/mt.h>
#endif
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
+#include <linux/errno.h>
+#include <soc/qcom/scm.h>
+enum subsystem {
+ TZ = 1,
+ APSS = 3
+};
+
+#define TZ_BLSP_MODIFY_OWNERSHIP_ID 3
+#endif
#include <linux/completion.h>
@@ -182,17 +192,18 @@ static ssize_t synaptics_rmi4_synad_pid_store(struct device *dev,
static ssize_t synaptics_rmi4_virtual_key_map_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf);
-struct synaptics_rmi4_f01_device_status {
- union {
- struct {
- unsigned char status_code:4;
- unsigned char reserved:2;
- unsigned char flash_prog:1;
- unsigned char unconfigured:1;
- } __packed;
- unsigned char data[1];
- };
-};
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data);
+
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
+static ssize_t synaptics_rmi4_secure_touch_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_secure_touch_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t synaptics_rmi4_secure_touch_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+#endif
struct synaptics_rmi4_f11_query_0_5 {
union {
@@ -705,6 +716,14 @@ static struct device_attribute attrs[] = {
__ATTR(wake_gesture, 0664,
synaptics_rmi4_wake_gesture_show,
synaptics_rmi4_wake_gesture_store),
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
+ __ATTR(secure_touch_enable, 0664,
+ synaptics_rmi4_secure_touch_enable_show,
+ synaptics_rmi4_secure_touch_enable_store),
+ __ATTR(secure_touch, 0444,
+ synaptics_rmi4_secure_touch_show,
+ NULL),
+#endif
#ifdef USE_DATA_SERVER
__ATTR(synad_pid, 0220,
synaptics_rmi4_show_error,
@@ -720,6 +739,224 @@ static struct kobj_attribute virtual_key_map_attr = {
.show = synaptics_rmi4_virtual_key_map_show,
};
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
+static int synaptics_rmi4_i2c_change_pipe_owner(
+ struct synaptics_rmi4_data *rmi4_data, enum subsystem subsystem)
+{
+ struct scm_desc desc;
+ struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
+ int ret = 0;
+
+ desc.arginfo = SCM_ARGS(2);
+ desc.args[0] = i2c->adapter->nr;
+ desc.args[1] = subsystem;
+
+ ret = scm_call2(SCM_SIP_FNID(SCM_SVC_TZ, TZ_BLSP_MODIFY_OWNERSHIP_ID),
+ &desc);
+ if (ret) {
+ dev_err(rmi4_data->pdev->dev.parent, "%s: failed\n",
+ __func__);
+ return ret;
+ }
+
+ return desc.ret[0];
+}
+
+static void synaptics_rmi4_secure_touch_init(struct synaptics_rmi4_data *data)
+{
+ data->st_initialized = false;
+ init_completion(&data->st_powerdown);
+ init_completion(&data->st_irq_processed);
+
+ /* Get clocks */
+ data->core_clk = devm_clk_get(data->pdev->dev.parent, "core_clk");
+ if (IS_ERR(data->core_clk)) {
+ dev_dbg(data->pdev->dev.parent,
+ "%s: error on clk_get(core_clk): %ld\n",
+ __func__, PTR_ERR(data->core_clk));
+ data->core_clk = NULL;
+ }
+
+ data->iface_clk = devm_clk_get(data->pdev->dev.parent, "iface_clk");
+ if (IS_ERR(data->iface_clk)) {
+ dev_dbg(data->pdev->dev.parent,
+ "%s: error on clk_get(iface_clk): %ld\n",
+ __func__, PTR_ERR(data->iface_clk));
+ data->iface_clk = NULL;
+ }
+ data->st_initialized = true;
+}
+
+static void synaptics_rmi4_secure_touch_notify(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ sysfs_notify(&rmi4_data->input_dev->dev.kobj, NULL, "secure_touch");
+}
+
+static irqreturn_t synaptics_rmi4_filter_interrupt(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ if (atomic_read(&rmi4_data->st_enabled)) {
+ if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 0, 1) == 0) {
+ reinit_completion(&rmi4_data->st_irq_processed);
+ synaptics_rmi4_secure_touch_notify(rmi4_data);
+ wait_for_completion_interruptible(
+ &rmi4_data->st_irq_processed);
+ }
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+/*
+ * 'blocking' variable will have value 'true' when we want to prevent the driver
+ * from accessing the xPU/SMMU protected HW resources while the session is
+ * active.
+ */
+static void synaptics_rmi4_secure_touch_stop(
+ struct synaptics_rmi4_data *rmi4_data, bool blocking)
+{
+ if (atomic_read(&rmi4_data->st_enabled)) {
+ atomic_set(&rmi4_data->st_pending_irqs, -1);
+ synaptics_rmi4_secure_touch_notify(rmi4_data);
+ if (blocking)
+ wait_for_completion_interruptible(
+ &rmi4_data->st_powerdown);
+ }
+}
+
+#else
+static void synaptics_rmi4_secure_touch_init(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+}
+
+static irqreturn_t synaptics_rmi4_filter_interrupt(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ return IRQ_NONE;
+}
+
+static void synaptics_rmi4_secure_touch_stop(
+ struct synaptics_rmi4_data *rmi4_data, bool blocking)
+{
+}
+#endif
+
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
+static ssize_t synaptics_rmi4_secure_touch_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%d",
+ atomic_read(&rmi4_data->st_enabled));
+}
+
+/*
+ * Accept only "0" and "1" valid values.
+ * "0" will reset the st_enabled flag, then wake up the reading process and
+ * the interrupt handler.
+ * The bus driver is notified via pm_runtime that it is not required to stay
+ * awake anymore.
+ * It will also make sure the queue of events is emptied in the controller,
+ * in case a touch happened in between the secure touch being disabled and
+ * the local ISR being ungated.
+ * "1" will set the st_enabled flag and clear the st_pending_irqs flag.
+ * The bus driver is requested via pm_runtime to stay awake.
+ */
+static ssize_t synaptics_rmi4_secure_touch_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ unsigned long value;
+ int err = 0;
+
+ if (count > 2)
+ return -EINVAL;
+
+ if (!rmi4_data->st_initialized)
+ return -EIO;
+
+ err = kstrtoul(buf, 10, &value);
+ if (err)
+ return err;
+
+ err = count;
+
+ switch (value) {
+ case 0:
+ if (atomic_read(&rmi4_data->st_enabled) == 0)
+ break;
+ synaptics_rmi4_i2c_change_pipe_owner(rmi4_data, APSS);
+ synaptics_rmi4_bus_put(rmi4_data);
+ atomic_set(&rmi4_data->st_enabled, 0);
+ synaptics_rmi4_secure_touch_notify(rmi4_data);
+ complete(&rmi4_data->st_irq_processed);
+ synaptics_rmi4_irq(rmi4_data->irq, rmi4_data);
+ complete(&rmi4_data->st_powerdown);
+
+ break;
+ case 1:
+ if (atomic_read(&rmi4_data->st_enabled)) {
+ err = -EBUSY;
+ break;
+ }
+ synchronize_irq(rmi4_data->irq);
+
+ if (synaptics_rmi4_bus_get(rmi4_data) < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "synaptics_rmi4_bus_get failed\n");
+ err = -EIO;
+ break;
+ }
+ synaptics_rmi4_i2c_change_pipe_owner(rmi4_data, TZ);
+ reinit_completion(&rmi4_data->st_powerdown);
+ reinit_completion(&rmi4_data->st_irq_processed);
+ atomic_set(&rmi4_data->st_enabled, 1);
+ atomic_set(&rmi4_data->st_pending_irqs, 0);
+ break;
+ default:
+ dev_err(rmi4_data->pdev->dev.parent,
+ "unsupported value: %lu\n", value);
+ err = -EINVAL;
+ break;
+ }
+ return err;
+}
+
+/*
+ * This function returns whether there are pending interrupts, or
+ * other error conditions that need to be signaled to the userspace library,
+ * according tot he following logic:
+ * - st_enabled is 0 if secure touch is not enabled, returning -EBADF
+ * - st_pending_irqs is -1 to signal that secure touch is in being stopped,
+ * returning -EINVAL
+ * - st_pending_irqs is 1 to signal that there is a pending irq, returning
+ * the value "1" to the sysfs read operation
+ * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt
+ * has been processed, so the interrupt handler can be allowed to continue.
+ */
+static ssize_t synaptics_rmi4_secure_touch_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ int val = 0;
+
+ if (atomic_read(&rmi4_data->st_enabled) == 0)
+ return -EBADF;
+ if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, -1, 0) == -1)
+ return -EINVAL;
+ if (atomic_cmpxchg(&rmi4_data->st_pending_irqs, 1, 0) == 1)
+ val = 1;
+ else
+ complete(&rmi4_data->st_irq_processed);
+
+ return scnprintf(buf, PAGE_SIZE, "%u", val);
+}
+#endif
+
static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
@@ -1673,10 +1910,9 @@ static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data,
bool report)
{
int retval;
- unsigned char data[MAX_INTR_REGISTERS + 1];
- unsigned char *intr = &data[1];
+ unsigned char *data = NULL;
+ unsigned char *intr;
bool was_in_bl_mode;
- struct synaptics_rmi4_f01_device_status status;
struct synaptics_rmi4_fn *fhandler;
struct synaptics_rmi4_exp_fhandler *exp_fhandler;
struct synaptics_rmi4_device_info *rmi;
@@ -1687,6 +1923,14 @@ static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data,
* Get interrupt status information from F01 Data1 register to
* determine the source(s) that are flagging the interrupt.
*/
+ data = kcalloc((MAX_INTR_REGISTERS + 1), sizeof(char), GFP_KERNEL);
+ if (!data) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ intr = &data[1];
+
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f01_data_base_addr,
data,
@@ -1695,31 +1939,31 @@ static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data,
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read interrupt status\n",
__func__);
- return retval;
+ goto exit;
}
- status.data[0] = data[0];
- if (status.status_code == STATUS_CRC_IN_PROGRESS) {
+ rmi4_data->status.data[0] = data[0];
+ if (rmi4_data->status.status_code == STATUS_CRC_IN_PROGRESS) {
retval = synaptics_rmi4_check_status(rmi4_data,
&was_in_bl_mode);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to check status\n",
__func__);
- return retval;
+ goto exit;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f01_data_base_addr,
- status.data,
- sizeof(status.data));
+ rmi4_data->status.data,
+ sizeof(rmi4_data->status.data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read device status\n",
__func__);
- return retval;
+ goto exit;
}
}
- if (status.unconfigured && !status.flash_prog) {
+ if (rmi4_data->status.unconfigured && !rmi4_data->status.flash_prog) {
pr_notice("%s: spontaneous reset detected\n", __func__);
retval = synaptics_rmi4_reinit_device(rmi4_data);
if (retval < 0) {
@@ -1730,7 +1974,7 @@ static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data,
}
if (!report)
- return retval;
+ goto exit;
/*
* Traverse the function handler list and service the source(s)
@@ -1758,7 +2002,8 @@ static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data,
}
}
mutex_unlock(&exp_data.mutex);
-
+exit:
+ kfree(data);
return retval;
}
@@ -1768,6 +2013,9 @@ static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
const struct synaptics_dsx_board_data *bdata =
rmi4_data->hw_if->board_data;
+ if (synaptics_rmi4_filter_interrupt(data) == IRQ_HANDLED)
+ return IRQ_HANDLED;
+
if (gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state)
goto exit;
@@ -1816,12 +2064,18 @@ static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data,
bool enable, bool attn_only)
{
int retval = 0;
- unsigned char data[MAX_INTR_REGISTERS];
+ unsigned char *data = NULL;
const struct synaptics_dsx_board_data *bdata =
rmi4_data->hw_if->board_data;
mutex_lock(&(rmi4_data->rmi4_irq_enable_mutex));
+ data = kcalloc(MAX_INTR_REGISTERS, sizeof(char), GFP_KERNEL);
+ if (!data) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
if (attn_only) {
retval = synaptics_rmi4_int_enable(rmi4_data, enable);
goto exit;
@@ -1875,8 +2129,8 @@ static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data,
}
exit:
+ kfree(data);
mutex_unlock(&(rmi4_data->rmi4_irq_enable_mutex));
-
return retval;
}
@@ -2280,8 +2534,8 @@ static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
unsigned char subpacket;
unsigned char ctrl_23_size;
unsigned char size_of_2d_data;
- unsigned char size_of_query5;
- unsigned char size_of_query8;
+ unsigned char *size_of_query5 = NULL;
+ unsigned char *size_of_query8 = NULL;
unsigned char ctrl_8_offset;
unsigned char ctrl_20_offset;
unsigned char ctrl_23_offset;
@@ -2311,6 +2565,18 @@ static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data);
+ size_of_query5 = kcalloc(1, sizeof(char), GFP_KERNEL);
+ if (!size_of_query5) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ size_of_query8 = kcalloc(1, sizeof(char), GFP_KERNEL);
+ if (!size_of_query8) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
query_5 = kzalloc(sizeof(*query_5), GFP_KERNEL);
if (!query_5) {
dev_err(rmi4_data->pdev->dev.parent,
@@ -2367,19 +2633,19 @@ static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
retval = synaptics_rmi4_reg_read(rmi4_data,
fhandler->full_addr.query_base + 4,
- &size_of_query5,
- sizeof(size_of_query5));
+ size_of_query5,
+ sizeof(*size_of_query5));
if (retval < 0)
goto exit;
- if (size_of_query5 > sizeof(query_5->data))
- size_of_query5 = sizeof(query_5->data);
+ if (*size_of_query5 > sizeof(query_5->data))
+ *size_of_query5 = sizeof(query_5->data);
memset(query_5->data, 0x00, sizeof(query_5->data));
retval = synaptics_rmi4_reg_read(rmi4_data,
fhandler->full_addr.query_base + 5,
query_5->data,
- size_of_query5);
+ *size_of_query5);
if (retval < 0)
goto exit;
@@ -2494,26 +2760,26 @@ static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
retval = synaptics_rmi4_reg_read(rmi4_data,
fhandler->full_addr.query_base + 7,
- &size_of_query8,
- sizeof(size_of_query8));
+ size_of_query8,
+ sizeof(*size_of_query8));
if (retval < 0)
goto exit;
- if (size_of_query8 > sizeof(query_8->data))
- size_of_query8 = sizeof(query_8->data);
+ if (*size_of_query8 > sizeof(query_8->data))
+ *size_of_query8 = sizeof(query_8->data);
memset(query_8->data, 0x00, sizeof(query_8->data));
retval = synaptics_rmi4_reg_read(rmi4_data,
fhandler->full_addr.query_base + 8,
query_8->data,
- size_of_query8);
+ *size_of_query8);
if (retval < 0)
goto exit;
/* Determine the presence of the Data0 register */
extra_data->data1_offset = query_8->data0_is_present;
- if ((size_of_query8 >= 3) && (query_8->data15_is_present)) {
+ if ((*size_of_query8 >= 3) && (query_8->data15_is_present)) {
extra_data->data15_offset = query_8->data0_is_present +
query_8->data1_is_present +
query_8->data2_is_present +
@@ -2535,7 +2801,7 @@ static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
}
#ifdef REPORT_2D_PRESSURE
- if ((size_of_query8 >= 5) && (query_8->data29_is_present)) {
+ if ((*size_of_query8 >= 5) && (query_8->data29_is_present)) {
extra_data->data29_offset = query_8->data0_is_present +
query_8->data1_is_present +
query_8->data2_is_present +
@@ -2683,6 +2949,8 @@ static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
}
exit:
+ kfree(size_of_query5);
+ kfree(size_of_query8);
kfree(query_5);
kfree(query_8);
kfree(ctrl_8);
@@ -2908,16 +3176,15 @@ static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
{
int retval;
int timeout = CHECK_STATUS_TIMEOUT_MS;
- struct synaptics_rmi4_f01_device_status status;
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f01_data_base_addr,
- status.data,
- sizeof(status.data));
+ rmi4_data->status.data,
+ sizeof(rmi4_data->status.data));
if (retval < 0)
return retval;
- while (status.status_code == STATUS_CRC_IN_PROGRESS) {
+ while (rmi4_data->status.status_code == STATUS_CRC_IN_PROGRESS) {
if (timeout > 0)
msleep(20);
else
@@ -2925,8 +3192,8 @@ static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f01_data_base_addr,
- status.data,
- sizeof(status.data));
+ rmi4_data->status.data,
+ sizeof(rmi4_data->status.data));
if (retval < 0)
return retval;
@@ -2936,11 +3203,11 @@ static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
if (timeout != CHECK_STATUS_TIMEOUT_MS)
*was_in_bl_mode = true;
- if (status.flash_prog == 1) {
+ if (rmi4_data->status.flash_prog == 1) {
rmi4_data->flash_prog_mode = true;
pr_notice("%s: In flash prog mode, status = 0x%02x\n",
__func__,
- status.status_code);
+ rmi4_data->status.status_code);
} else {
rmi4_data->flash_prog_mode = false;
}
@@ -2951,32 +3218,39 @@ static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
static int synaptics_rmi4_set_configured(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
- unsigned char device_ctrl;
+ unsigned char *device_ctrl = NULL;
+
+ device_ctrl = kcalloc(1, sizeof(char), GFP_KERNEL);
+ if (!device_ctrl) {
+ retval = -ENOMEM;
+ goto exit;
+ }
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
+ device_ctrl,
+ sizeof(*device_ctrl));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to set configured\n",
__func__);
- return retval;
+ goto exit;
}
- rmi4_data->no_sleep_setting = device_ctrl & NO_SLEEP_ON;
- device_ctrl |= CONFIGURED;
+ rmi4_data->no_sleep_setting = *device_ctrl & NO_SLEEP_ON;
+ *device_ctrl |= CONFIGURED;
retval = synaptics_rmi4_reg_write(rmi4_data,
rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
+ device_ctrl,
+ sizeof(*device_ctrl));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to set configured\n",
__func__);
}
-
+exit:
+ kfree(device_ctrl);
return retval;
}
@@ -3013,7 +3287,6 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
bool f01found;
bool f35found;
bool was_in_bl_mode;
- struct synaptics_rmi4_fn_desc rmi_fd;
struct synaptics_rmi4_fn *fhandler;
struct synaptics_rmi4_device_info *rmi;
@@ -3034,8 +3307,8 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
retval = synaptics_rmi4_reg_read(rmi4_data,
pdt_entry_addr,
- (unsigned char *)&rmi_fd,
- sizeof(rmi_fd));
+ (unsigned char *)&rmi4_data->rmi_fd,
+ sizeof(rmi4_data->rmi_fd));
if (retval < 0)
return retval;
@@ -3043,7 +3316,7 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
fhandler = NULL;
- if (rmi_fd.fn_number == 0) {
+ if (rmi4_data->rmi_fd.fn_number == 0) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Reached end of PDT\n",
__func__);
@@ -3052,28 +3325,30 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: F%02x found (page %d)\n",
- __func__, rmi_fd.fn_number,
+ __func__, rmi4_data->rmi_fd.fn_number,
page_number);
- switch (rmi_fd.fn_number) {
+ switch (rmi4_data->rmi_fd.fn_number) {
case SYNAPTICS_RMI4_F01:
- if (rmi_fd.intr_src_count == 0)
+ if (rmi4_data->rmi_fd.intr_src_count == 0)
break;
f01found = true;
retval = synaptics_rmi4_alloc_fh(&fhandler,
- &rmi_fd, page_number);
+ &rmi4_data->rmi_fd,
+ page_number);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc for F%d\n",
- __func__,
- rmi_fd.fn_number);
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi4_data->rmi_fd.fn_number);
return retval;
}
retval = synaptics_rmi4_f01_init(rmi4_data,
- fhandler, &rmi_fd, intr_count);
+ fhandler, &rmi4_data->rmi_fd,
+ intr_count);
if (retval < 0)
return retval;
@@ -3097,59 +3372,65 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
break;
case SYNAPTICS_RMI4_F11:
- if (rmi_fd.intr_src_count == 0)
+ if (rmi4_data->rmi_fd.intr_src_count == 0)
break;
retval = synaptics_rmi4_alloc_fh(&fhandler,
- &rmi_fd, page_number);
+ &rmi4_data->rmi_fd,
+ page_number);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc for F%d\n",
- __func__,
- rmi_fd.fn_number);
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi4_data->rmi_fd.fn_number);
return retval;
}
retval = synaptics_rmi4_f11_init(rmi4_data,
- fhandler, &rmi_fd, intr_count);
+ fhandler, &rmi4_data->rmi_fd,
+ intr_count);
if (retval < 0)
return retval;
break;
case SYNAPTICS_RMI4_F12:
- if (rmi_fd.intr_src_count == 0)
+ if (rmi4_data->rmi_fd.intr_src_count == 0)
break;
retval = synaptics_rmi4_alloc_fh(&fhandler,
- &rmi_fd, page_number);
+ &rmi4_data->rmi_fd,
+ page_number);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc for F%d\n",
- __func__,
- rmi_fd.fn_number);
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi4_data->rmi_fd.fn_number);
return retval;
}
retval = synaptics_rmi4_f12_init(rmi4_data,
- fhandler, &rmi_fd, intr_count);
+ fhandler, &rmi4_data->rmi_fd,
+ intr_count);
if (retval < 0)
return retval;
break;
case SYNAPTICS_RMI4_F1A:
- if (rmi_fd.intr_src_count == 0)
+ if (rmi4_data->rmi_fd.intr_src_count == 0)
break;
retval = synaptics_rmi4_alloc_fh(&fhandler,
- &rmi_fd, page_number);
+ &rmi4_data->rmi_fd,
+ page_number);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc for F%d\n",
- __func__,
- rmi_fd.fn_number);
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi4_data->rmi_fd.fn_number);
return retval;
}
retval = synaptics_rmi4_f1a_init(rmi4_data,
- fhandler, &rmi_fd, intr_count);
+ fhandler, &rmi4_data->rmi_fd,
+ intr_count);
if (retval < 0) {
#ifdef IGNORE_FN_INIT_FAILURE
kfree(fhandler);
@@ -3161,25 +3442,27 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
break;
#ifdef USE_DATA_SERVER
case SYNAPTICS_RMI4_F21:
- if (rmi_fd.intr_src_count == 0)
+ if (rmi4_data->rmi_fd.intr_src_count == 0)
break;
retval = synaptics_rmi4_alloc_fh(&fhandler,
- &rmi_fd, page_number);
+ &rmi4_data->rmi_fd,
+ page_number);
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
- "%s: Failed to alloc for F%d\n",
- __func__,
- rmi_fd.fn_number);
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi4_data->rmi_fd.fn_number);
return retval;
}
- fhandler->fn_number = rmi_fd.fn_number;
+ fhandler->fn_number =
+ rmi4_data->rmi_fd.fn_number;
fhandler->num_of_data_sources =
- rmi_fd.intr_src_count;
+ rmi4_data->rmi_fd.intr_src_count;
- synaptics_rmi4_set_intr_mask(fhandler, &rmi_fd,
- intr_count);
+ synaptics_rmi4_set_intr_mask(fhandler,
+ &rmi4_data->rmi_fd, intr_count);
break;
#endif
case SYNAPTICS_RMI4_F35:
@@ -3188,16 +3471,16 @@ static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data)
#ifdef F51_DISCRETE_FORCE
case SYNAPTICS_RMI4_F51:
rmi4_data->f51_query_base_addr =
- rmi_fd.query_base_addr |
- (page_number << 8);
+ rmi4_data->rmi_fd.query_base_addr |
+ (page_number << 8);
break;
#endif
}
/* Accumulate the interrupt count */
- intr_count += rmi_fd.intr_src_count;
+ intr_count += rmi4_data->rmi_fd.intr_src_count;
- if (fhandler && rmi_fd.intr_src_count) {
+ if (fhandler && rmi4_data->rmi_fd.intr_src_count) {
list_add_tail(&fhandler->link,
&rmi->support_fn_list);
}
@@ -4114,39 +4397,47 @@ static int synaptics_rmi4_sleep_enable(struct synaptics_rmi4_data *rmi4_data,
bool enable)
{
int retval;
- unsigned char device_ctrl;
+ unsigned char *device_ctrl = NULL;
unsigned char no_sleep_setting = rmi4_data->no_sleep_setting;
+ device_ctrl = kcalloc(1, sizeof(char), GFP_KERNEL);
+ if (!device_ctrl) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
retval = synaptics_rmi4_reg_read(rmi4_data,
rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
+ device_ctrl,
+ sizeof(*device_ctrl));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read device control\n",
__func__);
- return retval;
+ goto exit;
}
- device_ctrl = device_ctrl & ~MASK_3BIT;
+ *device_ctrl = *device_ctrl & ~MASK_3BIT;
if (enable)
- device_ctrl = device_ctrl | SENSOR_SLEEP;
+ *device_ctrl = *device_ctrl | SENSOR_SLEEP;
else
- device_ctrl = device_ctrl | no_sleep_setting | NORMAL_OPERATION;
+ *device_ctrl = *device_ctrl | no_sleep_setting |
+ NORMAL_OPERATION;
retval = synaptics_rmi4_reg_write(rmi4_data,
rmi4_data->f01_ctrl_base_addr,
- &device_ctrl,
- sizeof(device_ctrl));
+ device_ctrl,
+ sizeof(*device_ctrl));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to write device control\n",
__func__);
- return retval;
+ goto exit;
}
rmi4_data->sensor_sleep = enable;
-
+exit:
+ kfree(device_ctrl);
return retval;
}
@@ -4291,6 +4582,11 @@ static int synaptics_rmi4_probe(struct platform_device *pdev)
goto err_drm_reg;
}
}
+
+ /* Initialize secure touch */
+ synaptics_rmi4_secure_touch_init(rmi4_data);
+ synaptics_rmi4_secure_touch_stop(rmi4_data, true);
+
rmi4_data->rmi4_probe_wq = create_singlethread_workqueue(
"Synaptics_rmi4_probe_wq");
if (!rmi4_data->rmi4_probe_wq) {
@@ -4641,6 +4937,7 @@ static int synaptics_rmi4_dsi_panel_notifier_cb(struct notifier_block *self,
if (evdata && evdata->data && rmi4_data) {
if (event == DRM_PANEL_EARLY_EVENT_BLANK) {
+ synaptics_rmi4_secure_touch_stop(rmi4_data, false);
transition = *(int *)evdata->data;
if (transition == DRM_PANEL_BLANK_POWERDOWN) {
if (rmi4_data->initialized)
@@ -4679,6 +4976,13 @@ static int synaptics_rmi4_early_suspend(struct early_suspend *h)
if (rmi4_data->stay_awake)
return retval;
+ /*
+ * During early suspend/late resume, the driver doesn't access xPU/SMMU
+ * protected HW resources. So, there is no compelling need to block,
+ * but notifying the userspace that a power event has occurred is
+ * enough. Hence 'blocking' variable can be set to false.
+ */
+ synaptics_rmi4_secure_touch_stop(rmi4_data, false);
if (rmi4_data->enable_wakeup_gesture) {
if (rmi4_data->no_sleep_setting) {
@@ -4743,6 +5047,8 @@ static int synaptics_rmi4_late_resume(struct early_suspend *h)
if (rmi4_data->stay_awake)
return retval;
+ synaptics_rmi4_secure_touch_stop(rmi4_data, false);
+
if (rmi4_data->enable_wakeup_gesture) {
disable_irq_wake(rmi4_data->irq);
goto exit;
@@ -4791,6 +5097,8 @@ static int synaptics_rmi4_suspend(struct device *dev)
if (rmi4_data->stay_awake)
return 0;
+ synaptics_rmi4_secure_touch_stop(rmi4_data, true);
+
if (rmi4_data->enable_wakeup_gesture) {
if (rmi4_data->no_sleep_setting) {
synaptics_rmi4_reg_read(rmi4_data,
@@ -4862,6 +5170,7 @@ static int synaptics_rmi4_resume(struct device *dev)
rmi4_data->hw_if->board_data;
if (rmi4_data->stay_awake)
return 0;
+ synaptics_rmi4_secure_touch_stop(rmi4_data, true);
if (rmi4_data->enable_wakeup_gesture) {
disable_irq_wake(rmi4_data->irq);
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
index 8f175e3..0a778b0 100755
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
@@ -51,6 +51,13 @@
#include <drm/drm_panel.h>
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
+#include <linux/completion.h>
+#include <linux/atomic.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#endif
+
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38))
#define KERNEL_ABOVE_2_6_38
#endif
@@ -282,6 +289,17 @@ struct synaptics_rmi4_device_info {
struct list_head support_fn_list;
};
+struct synaptics_rmi4_f01_device_status {
+ union {
+ struct {
+ unsigned char status_code:4;
+ unsigned char reserved:2;
+ unsigned char flash_prog:1;
+ unsigned char unconfigured:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
/*
* struct synaptics_rmi4_data - RMI4 device instance data
* @pdev: pointer to platform device
@@ -356,6 +374,8 @@ struct synaptics_rmi4_data {
const struct synaptics_dsx_hw_interface *hw_if;
struct synaptics_rmi4_device_info rmi4_mod_info;
struct synaptics_rmi4_input_settings input_settings;
+ struct synaptics_rmi4_fn_desc rmi_fd;
+ struct synaptics_rmi4_f01_device_status status;
struct kobject *board_prop_dir;
struct regulator *pwr_reg;
struct regulator *bus_reg;
@@ -431,6 +451,15 @@ struct synaptics_rmi4_data {
bool enable);
void (*report_touch)(struct synaptics_rmi4_data *rmi4_data,
struct synaptics_rmi4_fn *fhandler);
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
+ atomic_t st_enabled;
+ atomic_t st_pending_irqs;
+ struct completion st_powerdown;
+ struct completion st_irq_processed;
+ bool st_initialized;
+ struct clk *core_clk;
+ struct clk *iface_clk;
+#endif
};
struct synaptics_dsx_bus_access {
@@ -439,6 +468,8 @@ struct synaptics_dsx_bus_access {
unsigned char *data, unsigned int length);
int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
unsigned char *data, unsigned int length);
+ int (*get)(struct synaptics_rmi4_data *rmi4_data);
+ void (*put)(struct synaptics_rmi4_data *rmi4_data);
};
struct synaptics_dsx_hw_interface {
@@ -489,6 +520,16 @@ static inline int synaptics_rmi4_reg_write(
return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len);
}
+static inline int synaptics_rmi4_bus_get(struct synaptics_rmi4_data *rmi4_data)
+{
+ return rmi4_data->hw_if->bus_access->get(rmi4_data);
+}
+
+static inline void synaptics_rmi4_bus_put(struct synaptics_rmi4_data *rmi4_data)
+{
+ rmi4_data->hw_if->bus_access->put(rmi4_data);
+}
+
static inline ssize_t synaptics_rmi4_show_error(struct device *dev,
struct device_attribute *attr, char *buf)
{
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
index 2372368..6e78ebb 100755
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
@@ -1388,30 +1388,42 @@ static int fwu_parse_image_info(void)
static int fwu_read_flash_status(void)
{
- int retval;
- unsigned char status;
- unsigned char command;
+ int retval = 0;
+ unsigned char *status = NULL;
+ unsigned char *command = NULL;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ status = kcalloc(1, sizeof(char), GFP_KERNEL);
+ if (!status) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ command = kcalloc(1, sizeof(char), GFP_KERNEL);
+ if (!command) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.data_base_addr + fwu->off.flash_status,
- &status,
- sizeof(status));
+ status,
+ sizeof(*status));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash status\n",
__func__);
- return retval;
+ goto exit;
}
- fwu->in_bl_mode = status >> 7;
+ fwu->in_bl_mode = *status >> 7;
if (fwu->bl_version == BL_V5)
- fwu->flash_status = (status >> 4) & MASK_3BIT;
+ fwu->flash_status = (*status >> 4) & MASK_3BIT;
else if (fwu->bl_version == BL_V6)
- fwu->flash_status = status & MASK_3BIT;
+ fwu->flash_status = *status & MASK_3BIT;
else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- fwu->flash_status = status & MASK_5BIT;
+ fwu->flash_status = *status & MASK_5BIT;
if (fwu->write_bootloader)
fwu->flash_status = 0x00;
@@ -1429,26 +1441,28 @@ static int fwu_read_flash_status(void)
retval = synaptics_rmi4_reg_read(rmi4_data,
fwu->f34_fd.data_base_addr + fwu->off.flash_cmd,
- &command,
- sizeof(command));
+ command,
+ sizeof(*command));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash command\n",
__func__);
- return retval;
+ goto exit;
}
if (fwu->bl_version == BL_V5)
- fwu->command = command & MASK_4BIT;
+ fwu->command = *command & MASK_4BIT;
else if (fwu->bl_version == BL_V6)
- fwu->command = command & MASK_6BIT;
+ fwu->command = *command & MASK_6BIT;
else if (fwu->bl_version == BL_V7 || fwu->bl_version == BL_V8)
- fwu->command = command;
+ fwu->command = *command;
if (fwu->write_bootloader)
fwu->command = 0x00;
-
- return 0;
+exit:
+ kfree(status);
+ kfree(command);
+ return retval;
}
static int fwu_wait_for_idle(int timeout_ms, bool poll)
@@ -2062,10 +2076,22 @@ static int fwu_read_f34_v5v6_queries(void)
unsigned char count;
unsigned char base;
unsigned char offset;
- unsigned char buf[10];
- struct f34_v5v6_flash_properties_2 properties_2;
+ unsigned char *buf = NULL;
+ struct f34_v5v6_flash_properties_2 *properties_2 = NULL;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+ buf = kcalloc(10, sizeof(char), GFP_KERNEL);
+ if (!buf) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ properties_2 = kzalloc(sizeof(*properties_2), GFP_KERNEL);
+ if (!properties_2) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
base = fwu->f34_fd.query_base_addr;
retval = synaptics_rmi4_reg_read(rmi4_data,
@@ -2076,7 +2102,7 @@ static int fwu_read_f34_v5v6_queries(void)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read bootloader ID\n",
__func__);
- return retval;
+ goto exit;
}
if (fwu->bl_version == BL_V5) {
@@ -2103,7 +2129,7 @@ static int fwu_read_f34_v5v6_queries(void)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read block size info\n",
__func__);
- return retval;
+ goto exit;
}
batohs(&fwu->block_size, &(buf[0]));
@@ -2124,7 +2150,7 @@ static int fwu_read_f34_v5v6_queries(void)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash properties\n",
__func__);
- return retval;
+ goto exit;
}
count = 4;
@@ -2146,7 +2172,7 @@ static int fwu_read_f34_v5v6_queries(void)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read block count info\n",
__func__);
- return retval;
+ goto exit;
}
batohs(&fwu->blkcount.ui_firmware, &(buf[0]));
@@ -2178,17 +2204,17 @@ static int fwu_read_f34_v5v6_queries(void)
if (fwu->flash_properties.has_query4) {
retval = synaptics_rmi4_reg_read(rmi4_data,
base + fwu->off.properties_2,
- properties_2.data,
- sizeof(properties_2.data));
+ properties_2->data,
+ sizeof(properties_2->data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read flash properties 2\n",
__func__);
- return retval;
+ goto exit;
}
offset = fwu->off.properties_2 + 1;
count = 0;
- if (properties_2.has_guest_code) {
+ if (properties_2->has_guest_code) {
retval = synaptics_rmi4_reg_read(rmi4_data,
base + offset + count,
buf,
@@ -2197,7 +2223,7 @@ static int fwu_read_f34_v5v6_queries(void)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read guest code block count\n",
__func__);
- return retval;
+ goto exit;
}
batohs(&fwu->blkcount.guest_code, &(buf[0]));
@@ -2205,7 +2231,7 @@ static int fwu_read_f34_v5v6_queries(void)
fwu->has_guest_code = true;
}
#ifdef SYNA_TDDI
- if (properties_2.has_force_config) {
+ if (properties_2->has_force_config) {
retval = synaptics_rmi4_reg_read(rmi4_data,
base + offset + count,
buf,
@@ -2214,13 +2240,13 @@ static int fwu_read_f34_v5v6_queries(void)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read tddi force block count\n",
__func__);
- return retval;
+ goto exit;
}
batohs(&fwu->blkcount.tddi_force_config, &(buf[0]));
count++;
fwu->has_force_config = true;
}
- if (properties_2.has_lockdown_data) {
+ if (properties_2->has_lockdown_data) {
retval = synaptics_rmi4_reg_read(rmi4_data,
base + offset + count,
buf,
@@ -2229,13 +2255,13 @@ static int fwu_read_f34_v5v6_queries(void)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read tddi lockdown block count\n",
__func__);
- return retval;
+ goto exit;
}
batohs(&fwu->blkcount.tddi_lockdown_data, &(buf[0]));
count++;
fwu->has_lockdown_data = true;
}
- if (properties_2.has_lcm_data) {
+ if (properties_2->has_lcm_data) {
retval = synaptics_rmi4_reg_read(rmi4_data,
base + offset + count,
buf,
@@ -2244,13 +2270,13 @@ static int fwu_read_f34_v5v6_queries(void)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read tddi lcm block count\n",
__func__);
- return retval;
+ goto exit;
}
batohs(&fwu->blkcount.tddi_lcm_data, &(buf[0]));
count++;
fwu->has_lcm_data = true;
}
- if (properties_2.has_oem_data) {
+ if (properties_2->has_oem_data) {
retval = synaptics_rmi4_reg_read(rmi4_data,
base + offset + count,
buf,
@@ -2259,7 +2285,7 @@ static int fwu_read_f34_v5v6_queries(void)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read tddi oem block count\n",
__func__);
- return retval;
+ goto exit;
}
batohs(&fwu->blkcount.tddi_oem_data, &(buf[0]));
fwu->has_oem_data = true;
@@ -2268,8 +2294,10 @@ static int fwu_read_f34_v5v6_queries(void)
}
fwu->has_utility_param = false;
-
- return 0;
+exit:
+ kfree(properties_2);
+ kfree(buf);
+ return retval;
}
static int fwu_read_f34_queries(void)
@@ -2790,7 +2818,6 @@ static int fwu_scan_pdt(void)
bool f01found = false;
bool f34found = false;
bool f35found = false;
- struct synaptics_rmi4_fn_desc rmi_fd;
struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
fwu->in_ub_mode = false;
@@ -2798,38 +2825,38 @@ static int fwu_scan_pdt(void)
for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
retval = synaptics_rmi4_reg_read(rmi4_data,
addr,
- (unsigned char *)&rmi_fd,
- sizeof(rmi_fd));
+ (unsigned char *)&rmi4_data->rmi_fd,
+ sizeof(rmi4_data->rmi_fd));
if (retval < 0)
return retval;
- if (rmi_fd.fn_number) {
+ if (rmi4_data->rmi_fd.fn_number) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Found F%02x\n",
- __func__, rmi_fd.fn_number);
- switch (rmi_fd.fn_number) {
+ __func__, rmi4_data->rmi_fd.fn_number);
+ switch (rmi4_data->rmi_fd.fn_number) {
case SYNAPTICS_RMI4_F01:
f01found = true;
rmi4_data->f01_query_base_addr =
- rmi_fd.query_base_addr;
+ rmi4_data->rmi_fd.query_base_addr;
rmi4_data->f01_ctrl_base_addr =
- rmi_fd.ctrl_base_addr;
+ rmi4_data->rmi_fd.ctrl_base_addr;
rmi4_data->f01_data_base_addr =
- rmi_fd.data_base_addr;
+ rmi4_data->rmi_fd.data_base_addr;
rmi4_data->f01_cmd_base_addr =
- rmi_fd.cmd_base_addr;
+ rmi4_data->rmi_fd.cmd_base_addr;
break;
case SYNAPTICS_RMI4_F34:
f34found = true;
fwu->f34_fd.query_base_addr =
- rmi_fd.query_base_addr;
+ rmi4_data->rmi_fd.query_base_addr;
fwu->f34_fd.ctrl_base_addr =
- rmi_fd.ctrl_base_addr;
+ rmi4_data->rmi_fd.ctrl_base_addr;
fwu->f34_fd.data_base_addr =
- rmi_fd.data_base_addr;
+ rmi4_data->rmi_fd.data_base_addr;
- switch (rmi_fd.fn_version) {
+ switch (rmi4_data->rmi_fd.fn_version) {
case F34_V0:
fwu->bl_version = BL_V5;
break;
@@ -2847,7 +2874,7 @@ static int fwu_scan_pdt(void)
}
fwu->intr_mask = 0;
- intr_src = rmi_fd.intr_src_count;
+ intr_src = rmi4_data->rmi_fd.intr_src_count;
intr_off = intr_count % 8;
for (ii = intr_off;
ii < (intr_src + intr_off);
@@ -2858,20 +2885,20 @@ static int fwu_scan_pdt(void)
case SYNAPTICS_RMI4_F35:
f35found = true;
fwu->f35_fd.query_base_addr =
- rmi_fd.query_base_addr;
+ rmi4_data->rmi_fd.query_base_addr;
fwu->f35_fd.ctrl_base_addr =
- rmi_fd.ctrl_base_addr;
+ rmi4_data->rmi_fd.ctrl_base_addr;
fwu->f35_fd.data_base_addr =
- rmi_fd.data_base_addr;
+ rmi4_data->rmi_fd.data_base_addr;
fwu->f35_fd.cmd_base_addr =
- rmi_fd.cmd_base_addr;
+ rmi4_data->rmi_fd.cmd_base_addr;
break;
}
} else {
break;
}
- intr_count += rmi_fd.intr_src_count;
+ intr_count += rmi4_data->rmi_fd.intr_src_count;
}
if (!f01found || !f34found) {
@@ -5568,7 +5595,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
{
int retval;
unsigned char attr_count;
- struct pdt_properties pdt_props;
+ struct pdt_properties *pdt_props = NULL;
if (fwu) {
dev_dbg(rmi4_data->pdev->dev.parent,
@@ -5577,6 +5604,12 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
return 0;
}
+ pdt_props = kzalloc(sizeof(*pdt_props), GFP_KERNEL);
+ if (!pdt_props) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
fwu = kzalloc(sizeof(*fwu), GFP_KERNEL);
if (!fwu) {
dev_err(rmi4_data->pdev->dev.parent,
@@ -5599,13 +5632,13 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
retval = synaptics_rmi4_reg_read(rmi4_data,
PDT_PROPS,
- pdt_props.data,
- sizeof(pdt_props.data));
+ pdt_props->data,
+ sizeof(pdt_props->data));
if (retval < 0) {
dev_dbg(rmi4_data->pdev->dev.parent,
"%s: Failed to read PDT properties, assuming 0x00\n",
__func__);
- } else if (pdt_props.has_bsr) {
+ } else if (pdt_props->has_bsr) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Reflash for LTS not currently supported\n",
__func__);
@@ -5697,6 +5730,7 @@ static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
fwu = NULL;
exit:
+ kfree(pdt_props);
return retval;
}
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
index 00ea777..9b75990 100644
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
@@ -273,11 +273,17 @@ static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data,
{
int retval = 0;
unsigned char retry;
- unsigned char buf[PAGE_SELECT_LEN];
+ unsigned char *buf = NULL;
unsigned char page;
struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
struct i2c_msg msg[2];
+ buf = kcalloc(PAGE_SELECT_LEN, sizeof(char), GFP_KERNEL);
+ if (!buf) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
msg[0].addr = hw_if.board_data->i2c_addr;
msg[0].flags = 0;
msg[0].len = PAGE_SELECT_LEN;
@@ -308,6 +314,8 @@ static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data,
retval = PAGE_SELECT_LEN;
}
+exit:
+ kfree(buf);
return retval;
}
@@ -316,7 +324,7 @@ static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
{
int retval = 0;
unsigned char retry;
- unsigned char buf;
+ unsigned char *buf = NULL;
unsigned char index = 0;
unsigned char xfer_msgs;
unsigned char remaining_msgs;
@@ -329,6 +337,12 @@ static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+ buf = kcalloc(1, sizeof(char), GFP_KERNEL);
+ if (!buf) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
if (retval != PAGE_SELECT_LEN) {
retval = -EIO;
@@ -338,13 +352,13 @@ static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
msg[0].addr = hw_if.board_data->i2c_addr;
msg[0].flags = 0;
msg[0].len = 1;
- msg[0].buf = &buf;
+ msg[0].buf = buf;
msg[rd_msgs].addr = hw_if.board_data->i2c_addr;
msg[rd_msgs].flags = I2C_M_RD;
msg[rd_msgs].len = (unsigned short)remaining_length;
msg[rd_msgs].buf = &data[data_offset];
- buf = addr & MASK_8BIT;
+ buf[0] = addr & MASK_8BIT;
remaining_msgs = rd_msgs + 1;
@@ -383,8 +397,8 @@ static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
retval = length;
exit:
+ kfree(buf);
mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
-
return retval;
}
@@ -504,10 +518,73 @@ static int check_default_tp(struct device_node *dt, const char *prop)
return ret;
}
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
+static int synaptics_rmi4_clk_prepare_enable(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ int ret;
+
+ ret = clk_prepare_enable(rmi4_data->iface_clk);
+ if (ret) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "error on clk_prepare_enable(iface_clk):%d\n",
+ ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(rmi4_data->core_clk);
+ if (ret) {
+ clk_disable_unprepare(rmi4_data->iface_clk);
+ dev_err(rmi4_data->pdev->dev.parent,
+ "error clk_prepare_enable(core_clk):%d\n", ret);
+ }
+ return ret;
+}
+
+static void synaptics_rmi4_clk_disable_unprepare(
+ struct synaptics_rmi4_data *rmi4_data)
+{
+ clk_disable_unprepare(rmi4_data->core_clk);
+ clk_disable_unprepare(rmi4_data->iface_clk);
+}
+
+static int synaptics_rmi4_i2c_get(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
+
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+ retval = pm_runtime_get_sync(i2c->adapter->dev.parent);
+ if (retval >= 0 && rmi4_data->core_clk != NULL &&
+ rmi4_data->iface_clk != NULL) {
+ retval = synaptics_rmi4_clk_prepare_enable(rmi4_data);
+ if (retval)
+ pm_runtime_put_sync(i2c->adapter->dev.parent);
+ }
+ mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+ return retval;
+}
+
+static void synaptics_rmi4_i2c_put(struct synaptics_rmi4_data *rmi4_data)
+{
+ struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
+
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+ if (rmi4_data->core_clk != NULL && rmi4_data->iface_clk != NULL)
+ synaptics_rmi4_clk_disable_unprepare(rmi4_data);
+ pm_runtime_put_sync(i2c->adapter->dev.parent);
+ mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
+}
+#endif
+
static struct synaptics_dsx_bus_access bus_access = {
.type = BUS_I2C,
.read = synaptics_rmi4_i2c_read,
.write = synaptics_rmi4_i2c_write,
+#if defined(CONFIG_SECURE_TOUCH_SYNAPTICS_DSX)
+ .get = synaptics_rmi4_i2c_get,
+ .put = synaptics_rmi4_i2c_put,
+#endif
};
static void synaptics_rmi4_i2c_dev_release(struct device *dev)
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_test_reporting.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_test_reporting.c
index c17b692..3a0be3c 100755
--- a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_test_reporting.c
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_test_reporting.c
@@ -4673,25 +4673,31 @@ static void test_f55_init(struct synaptics_rmi4_data *rmi4_data)
unsigned char ii;
unsigned char rx_electrodes;
unsigned char tx_electrodes;
- struct f55_control_43 ctrl_43;
+ struct f55_control_43 *ctrl_43 = NULL;
+
+ ctrl_43 = kzalloc(sizeof(*ctrl_43), GFP_KERNEL);
+ if (!ctrl_43) {
+ retval = -ENOMEM;
+ goto exit;
+ }
retval = test_f55_set_queries();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read F55 query registers\n",
__func__);
- return;
+ goto exit;
}
if (!f55->query.has_sensor_assignment)
- return;
+ goto exit;
retval = test_f55_set_controls();
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to set up F55 control registers\n",
__func__);
- return;
+ goto exit;
}
tx_electrodes = f55->query.num_of_tx_electrodes;
@@ -4708,7 +4714,7 @@ static void test_f55_init(struct synaptics_rmi4_data *rmi4_data)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read F55 tx assignment\n",
__func__);
- return;
+ goto exit;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
@@ -4719,7 +4725,7 @@ static void test_f55_init(struct synaptics_rmi4_data *rmi4_data)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read F55 rx assignment\n",
__func__);
- return;
+ goto exit;
}
f54->tx_assigned = 0;
@@ -4742,17 +4748,17 @@ static void test_f55_init(struct synaptics_rmi4_data *rmi4_data)
if (f55->extended_amp) {
retval = synaptics_rmi4_reg_read(rmi4_data,
f55->control_base_addr + f55->afe_mux_offset,
- ctrl_43.data,
- sizeof(ctrl_43.data));
+ ctrl_43->data,
+ sizeof(ctrl_43->data));
if (retval < 0) {
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read F55 AFE mux sizes\n",
__func__);
- return;
+ goto exit;
}
- f54->tx_assigned = ctrl_43.afe_l_mux_size +
- ctrl_43.afe_r_mux_size;
+ f54->tx_assigned = ctrl_43->afe_l_mux_size +
+ ctrl_43->afe_r_mux_size;
}
/* force mapping */
@@ -4768,7 +4774,7 @@ static void test_f55_init(struct synaptics_rmi4_data *rmi4_data)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read F55 force tx assignment\n",
__func__);
- return;
+ goto exit;
}
retval = synaptics_rmi4_reg_read(rmi4_data,
@@ -4779,7 +4785,7 @@ static void test_f55_init(struct synaptics_rmi4_data *rmi4_data)
dev_err(rmi4_data->pdev->dev.parent,
"%s: Failed to read F55 force rx assignment\n",
__func__);
- return;
+ goto exit;
}
for (ii = 0; ii < tx_electrodes; ii++) {
@@ -4792,6 +4798,10 @@ static void test_f55_init(struct synaptics_rmi4_data *rmi4_data)
f54->rx_assigned++;
}
}
+
+exit:
+ kfree(ctrl_43);
+ return;
}
static void test_f55_set_regs(struct synaptics_rmi4_data *rmi4_data,
@@ -4981,7 +4991,6 @@ static int test_scan_pdt(void)
unsigned short addr;
bool f54found = false;
bool f55found = false;
- struct synaptics_rmi4_fn_desc rmi_fd;
struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;
for (page = 0; page < PAGES_TO_SERVICE; page++) {
@@ -4990,30 +4999,31 @@ static int test_scan_pdt(void)
retval = synaptics_rmi4_reg_read(rmi4_data,
addr,
- (unsigned char *)&rmi_fd,
- sizeof(rmi_fd));
+ (unsigned char *)&rmi4_data->rmi_fd,
+ sizeof(rmi4_data->rmi_fd));
if (retval < 0)
return retval;
addr &= ~(MASK_8BIT << 8);
- if (!rmi_fd.fn_number)
+ if (!rmi4_data->rmi_fd.fn_number)
break;
- switch (rmi_fd.fn_number) {
+ switch (rmi4_data->rmi_fd.fn_number) {
case SYNAPTICS_RMI4_F54:
test_f54_set_regs(rmi4_data,
- &rmi_fd, intr_count, page);
+ &rmi4_data->rmi_fd, intr_count,
+ page);
f54found = true;
break;
case SYNAPTICS_RMI4_F55:
test_f55_set_regs(rmi4_data,
- &rmi_fd, page);
+ &rmi4_data->rmi_fd, page);
f55found = true;
break;
case SYNAPTICS_RMI4_F21:
test_f21_set_regs(rmi4_data,
- &rmi_fd, page);
+ &rmi4_data->rmi_fd, page);
break;
default:
break;
@@ -5022,7 +5032,7 @@ static int test_scan_pdt(void)
if (f54found && f55found)
goto pdt_done;
- intr_count += rmi_fd.intr_src_count;
+ intr_count += rmi4_data->rmi_fd.intr_src_count;
}
}
diff --git a/drivers/mmc/host/cqhci.c b/drivers/mmc/host/cqhci.c
index 3c7a511..b94402a 100644
--- a/drivers/mmc/host/cqhci.c
+++ b/drivers/mmc/host/cqhci.c
@@ -102,6 +102,10 @@ static void cqhci_set_irqs(struct cqhci_host *cq_host, u32 set)
static void cqhci_dumpregs(struct cqhci_host *cq_host)
{
struct mmc_host *mmc = cq_host->mmc;
+ int offset = 0;
+
+ if (cq_host->offset_changed)
+ offset = CQE_V5_VENDOR_CFG;
mmc_log_string(mmc,
"CQHCI_CTL=0x%08x CQHCI_IS=0x%08x CQHCI_ISTE=0x%08x CQHCI_ISGE=0x%08x CQHCI_TDBR=0x%08x CQHCI_TCN=0x%08x CQHCI_DQS=0x%08x CQHCI_DPT=0x%08x CQHCI_TERRI=0x%08x CQHCI_CRI=0x%08x CQHCI_CRA=0x%08x CQHCI_CRDCT=0x%08x\n",
@@ -147,6 +151,8 @@ static void cqhci_dumpregs(struct cqhci_host *cq_host)
CQHCI_DUMP("Resp idx: 0x%08x | Resp arg: 0x%08x\n",
cqhci_readl(cq_host, CQHCI_CRI),
cqhci_readl(cq_host, CQHCI_CRA));
+ CQHCI_DUMP("Vendor cfg 0x%08x\n",
+ cqhci_readl(cq_host, CQHCI_VENDOR_CFG + offset));
if (cq_host->ops->dumpregs)
cq_host->ops->dumpregs(mmc);
@@ -279,6 +285,12 @@ static void __cqhci_enable(struct cqhci_host *cq_host)
cq_host->caps |= CQHCI_CAP_CRYPTO_SUPPORT |
CQHCI_TASK_DESC_SZ_128;
cqcfg |= CQHCI_ICE_ENABLE;
+ /*
+ * For SDHC v5.0 onwards, ICE 3.0 specific registers are added
+ * in CQ register space, due to which few CQ registers are
+ * shifted. Set offset_changed boolean to use updated address.
+ */
+ cq_host->offset_changed = true;
}
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
@@ -698,8 +710,12 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
cq_host->qcnt += 1;
+ /* Ensure the task descriptor list is flushed before ringing doorbell */
+ wmb();
mmc_log_string(mmc, "tag: %d\n", tag);
cqhci_writel(cq_host, 1 << tag, CQHCI_TDBR);
+ /* Commit the doorbell write immediately */
+ wmb();
if (!(cqhci_readl(cq_host, CQHCI_TDBR) & (1 << tag)))
pr_debug("%s: cqhci: doorbell not set for tag %d\n",
mmc_hostname(mmc), tag);
@@ -809,8 +825,10 @@ static void cqhci_finish_mrq(struct mmc_host *mmc, unsigned int tag)
struct cqhci_slot *slot = &cq_host->slot[tag];
struct mmc_request *mrq = slot->mrq;
struct mmc_data *data;
- int err = 0;
+ int err = 0, offset = 0;
+ if (cq_host->offset_changed)
+ offset = CQE_V5_VENDOR_CFG;
if (!mrq) {
WARN_ONCE(1, "%s: cqhci: spurious TCN for tag %d\n",
mmc_hostname(mmc), tag);
@@ -840,6 +858,11 @@ static void cqhci_finish_mrq(struct mmc_host *mmc, unsigned int tag)
data->bytes_xfered = 0;
else
data->bytes_xfered = data->blksz * data->blocks;
+ } else {
+ cqhci_writel(cq_host, cqhci_readl(cq_host,
+ CQHCI_VENDOR_CFG + offset) |
+ CMDQ_SEND_STATUS_TRIGGER,
+ CQHCI_VENDOR_CFG + offset);
}
if (!(cq_host->caps & CQHCI_CAP_CRYPTO_SUPPORT) &&
diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h
index e87e9bc..da4324b 100644
--- a/drivers/mmc/host/cqhci.h
+++ b/drivers/mmc/host/cqhci.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2015-2019, 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
@@ -116,6 +117,14 @@
/* command response argument */
#define CQHCI_CRA 0x5C
+/*
+ * Add new macro for updated CQ vendor specific
+ * register address for SDHC v5.0 onwards.
+ */
+#define CQE_V5_VENDOR_CFG 0x900
+#define CQHCI_VENDOR_CFG 0x100
+#define CMDQ_SEND_STATUS_TRIGGER (1 << 31)
+
#define CQHCI_INT_ALL 0xF
#define CQHCI_IC_DEFAULT_ICCTH 31
#define CQHCI_IC_DEFAULT_ICTOVAL 1
@@ -183,6 +192,7 @@ struct cqhci_host {
bool activated;
bool waiting_for_idle;
bool recovery_halt;
+ bool offset_changed;
size_t desc_size;
size_t data_size;
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index ee219c7..f25c9cd 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -2400,6 +2400,7 @@ static int sdhci_msm_cqe_add_host(struct sdhci_host *host,
msm_host->mmc->caps2 |= MMC_CAP2_CQE;
cq_host->ops = &sdhci_msm_cqhci_ops;
+ msm_host->cq_host = cq_host;
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
if (dma64)
@@ -3950,6 +3951,46 @@ static void sdhci_msm_cache_debug_data(struct sdhci_host *host)
sizeof(struct sdhci_host));
}
+#define MAX_TEST_BUS 60
+#define DRV_NAME "cqhci-host"
+static void sdhci_msm_cqe_dump_debug_ram(struct sdhci_host *host)
+{
+ int i = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ const struct sdhci_msm_offset *msm_host_offset =
+ msm_host->offset;
+ struct cqhci_host *cq_host;
+ u32 version;
+ u16 minor;
+ int offset;
+
+ if (msm_host->cq_host)
+ cq_host = msm_host->cq_host;
+ else
+ return;
+
+ version = sdhci_msm_readl_relaxed(host,
+ msm_host_offset->CORE_MCI_VERSION);
+ minor = version & CORE_VERSION_TARGET_MASK;
+ /* registers offset changed starting from 4.2.0 */
+ offset = minor >= SDHCI_MSM_VER_420 ? 0 : 0x48;
+
+ if (cq_host->offset_changed)
+ offset += CQE_V5_VENDOR_CFG;
+ pr_err("---- Debug RAM dump ----\n");
+ pr_err(DRV_NAME ": Debug RAM wrap-around: 0x%08x | Debug RAM overlap: 0x%08x\n",
+ cqhci_readl(cq_host, CQ_CMD_DBG_RAM_WA + offset),
+ cqhci_readl(cq_host, CQ_CMD_DBG_RAM_OL + offset));
+
+ while (i < 16) {
+ pr_err(DRV_NAME ": Debug RAM dump [%d]: 0x%08x\n", i,
+ cqhci_readl(cq_host, CQ_CMD_DBG_RAM + offset + (4 * i)));
+ i++;
+ }
+ pr_err("-------------------------\n");
+}
+
void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -3964,6 +4005,8 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
sdhci_msm_cache_debug_data(host);
pr_info("----------- VENDOR REGISTER DUMP -----------\n");
+ if (msm_host->cq_host)
+ sdhci_msm_cqe_dump_debug_ram(host);
mmc_log_string(host->mmc, "Data cnt: 0x%08x | Fifo cnt: 0x%08x\n",
sdhci_msm_readl_relaxed(host,
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 58f5632a..051dfa8 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -225,6 +225,7 @@ struct sdhci_msm_host {
atomic_t clks_on; /* Set if clocks are enabled */
struct sdhci_msm_pltfm_data *pdata;
struct mmc_host *mmc;
+ struct cqhci_host *cq_host;
struct sdhci_msm_debug_data cached_data;
struct sdhci_pltfm_data sdhci_msm_pdata;
u32 curr_pwr_state;
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c
index 40238e6..e39a151 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_descriptor.c
@@ -34,8 +34,9 @@ rmnet_get_frag_descriptor(struct rmnet_port *port)
{
struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
struct rmnet_frag_descriptor *frag_desc;
+ unsigned long flags;
- spin_lock(&port->desc_pool_lock);
+ spin_lock_irqsave(&port->desc_pool_lock, flags);
if (!list_empty(&pool->free_list)) {
frag_desc = list_first_entry(&pool->free_list,
struct rmnet_frag_descriptor,
@@ -52,7 +53,7 @@ rmnet_get_frag_descriptor(struct rmnet_port *port)
}
out:
- spin_unlock(&port->desc_pool_lock);
+ spin_unlock_irqrestore(&port->desc_pool_lock, flags);
return frag_desc;
}
EXPORT_SYMBOL(rmnet_get_frag_descriptor);
@@ -62,6 +63,7 @@ void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc,
{
struct rmnet_frag_descriptor_pool *pool = port->frag_desc_pool;
struct page *page = skb_frag_page(&frag_desc->frag);
+ unsigned long flags;
list_del(&frag_desc->list);
if (page)
@@ -70,9 +72,9 @@ void rmnet_recycle_frag_descriptor(struct rmnet_frag_descriptor *frag_desc,
memset(frag_desc, 0, sizeof(*frag_desc));
INIT_LIST_HEAD(&frag_desc->list);
INIT_LIST_HEAD(&frag_desc->sub_frags);
- spin_lock(&port->desc_pool_lock);
+ spin_lock_irqsave(&port->desc_pool_lock, flags);
list_add_tail(&frag_desc->list, &pool->free_list);
- spin_unlock(&port->desc_pool_lock);
+ spin_unlock_irqrestore(&port->desc_pool_lock, flags);
}
EXPORT_SYMBOL(rmnet_recycle_frag_descriptor);
@@ -670,6 +672,12 @@ static void __rmnet_frag_segment_data(struct rmnet_frag_descriptor *coal_desc,
new_frag->tcp_seq_set = 1;
new_frag->tcp_seq = htonl(ntohl(th->seq) +
coal_desc->data_offset);
+ } else if (coal_desc->trans_proto == IPPROTO_UDP) {
+ struct udphdr *uh;
+
+ uh = (struct udphdr *)(hdr_start + coal_desc->ip_len);
+ if (coal_desc->ip_proto == 4 && !uh->check)
+ csum_valid = true;
}
if (coal_desc->ip_proto == 4) {
@@ -734,6 +742,7 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc,
u8 pkt, total_pkt = 0;
u8 nlo;
bool gro = coal_desc->dev->features & NETIF_F_GRO_HW;
+ bool zero_csum = false;
/* Pull off the headers we no longer need */
if (!rmnet_frag_pull(coal_desc, port, sizeof(struct rmnet_map_header)))
@@ -794,7 +803,12 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc,
th = (struct tcphdr *)((u8 *)iph + coal_desc->ip_len);
coal_desc->trans_len = th->doff * 4;
} else if (coal_desc->trans_proto == IPPROTO_UDP) {
- coal_desc->trans_len = sizeof(struct udphdr);
+ struct udphdr *uh;
+
+ uh = (struct udphdr *)((u8 *)iph + coal_desc->ip_len);
+ coal_desc->trans_len = sizeof(*uh);
+ if (coal_desc->ip_proto == 4 && !uh->check)
+ zero_csum = true;
} else {
priv->stats.coal.coal_trans_invalid++;
return;
@@ -802,7 +816,7 @@ rmnet_frag_segment_coal_data(struct rmnet_frag_descriptor *coal_desc,
coal_desc->hdrs_valid = 1;
- if (rmnet_map_v5_csum_buggy(coal_hdr)) {
+ if (rmnet_map_v5_csum_buggy(coal_hdr) && !zero_csum) {
/* Mark the checksum as valid if it checks out */
if (rmnet_frag_validate_csum(coal_desc))
coal_desc->csum_valid = true;
diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
index 84a42c9..da5e47c 100644
--- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
+++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c
@@ -716,6 +716,7 @@ __rmnet_map_segment_coal_skb(struct sk_buff *coal_skb,
struct rmnet_priv *priv = netdev_priv(coal_skb->dev);
__sum16 *check = NULL;
u32 alloc_len;
+ bool zero_csum = false;
/* We can avoid copying the data if the SKB we got from the lower-level
* drivers was nonlinear.
@@ -747,6 +748,8 @@ __rmnet_map_segment_coal_skb(struct sk_buff *coal_skb,
uh->len = htons(skbn->len);
check = &uh->check;
+ if (coal_meta->ip_proto == 4 && !uh->check)
+ zero_csum = true;
}
/* Push IP header and update necessary fields */
@@ -767,7 +770,7 @@ __rmnet_map_segment_coal_skb(struct sk_buff *coal_skb,
}
/* Handle checksum status */
- if (likely(csum_valid)) {
+ if (likely(csum_valid) || zero_csum) {
/* Set the partial checksum information */
rmnet_map_partial_csum(skbn, coal_meta);
} else if (check) {
@@ -865,6 +868,7 @@ static void rmnet_map_segment_coal_skb(struct sk_buff *coal_skb,
u8 pkt, total_pkt = 0;
u8 nlo;
bool gro = coal_skb->dev->features & NETIF_F_GRO_HW;
+ bool zero_csum = false;
memset(&coal_meta, 0, sizeof(coal_meta));
@@ -926,12 +930,15 @@ static void rmnet_map_segment_coal_skb(struct sk_buff *coal_skb,
uh = (struct udphdr *)((u8 *)iph + coal_meta.ip_len);
coal_meta.trans_len = sizeof(*uh);
coal_meta.trans_header = uh;
+ /* Check for v4 zero checksum */
+ if (coal_meta.ip_proto == 4 && !uh->check)
+ zero_csum = true;
} else {
priv->stats.coal.coal_trans_invalid++;
return;
}
- if (rmnet_map_v5_csum_buggy(coal_hdr)) {
+ if (rmnet_map_v5_csum_buggy(coal_hdr) && !zero_csum) {
rmnet_map_move_headers(coal_skb);
/* Mark as valid if it checks out */
if (rmnet_map_validate_csum(coal_skb, &coal_meta))
diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c
index 5601883..5aa68e3 100644
--- a/drivers/net/wireless/cnss2/bus.c
+++ b/drivers/net/wireless/cnss2/bus.c
@@ -186,6 +186,36 @@ int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv)
}
}
+int cnss_bus_qmi_send_get(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_qmi_send_get(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
+int cnss_bus_qmi_send_put(struct cnss_plat_data *plat_priv)
+{
+ if (!plat_priv)
+ return -ENODEV;
+
+ switch (plat_priv->bus_type) {
+ case CNSS_BUS_PCI:
+ return cnss_pci_qmi_send_put(plat_priv->bus_priv);
+ default:
+ cnss_pr_err("Unsupported bus type: %d\n",
+ plat_priv->bus_type);
+ return -EINVAL;
+ }
+}
+
void cnss_bus_fw_boot_timeout_hdlr(struct timer_list *t)
{
struct cnss_plat_data *plat_priv =
diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h
index 8ec4887..5248eb5 100644
--- a/drivers/net/wireless/cnss2/bus.h
+++ b/drivers/net/wireless/cnss2/bus.h
@@ -30,6 +30,8 @@ int cnss_bus_alloc_qdss_mem(struct cnss_plat_data *plat_priv);
void cnss_bus_free_qdss_mem(struct cnss_plat_data *plat_priv);
u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv);
int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv);
+int cnss_bus_qmi_send_get(struct cnss_plat_data *plat_priv);
+int cnss_bus_qmi_send_put(struct cnss_plat_data *plat_priv);
void cnss_bus_fw_boot_timeout_hdlr(struct timer_list *t);
void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv,
bool in_panic);
diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c
index a1e0dc5..bb55c31 100644
--- a/drivers/net/wireless/cnss2/debug.c
+++ b/drivers/net/wireless/cnss2/debug.c
@@ -477,6 +477,10 @@ static ssize_t cnss_runtime_pm_debug_write(struct file *fp,
cnss_pci_pm_runtime_put_noidle(pci_priv);
} else if (sysfs_streq(cmd, "mark_last_busy")) {
cnss_pci_pm_runtime_mark_last_busy(pci_priv);
+ } else if (sysfs_streq(cmd, "resume_bus")) {
+ cnss_pci_resume_bus(pci_priv);
+ } else if (sysfs_streq(cmd, "suspend_bus")) {
+ cnss_pci_suspend_bus(pci_priv);
} else {
cnss_pr_err("Runtime PM debugfs command is invalid\n");
ret = -EINVAL;
@@ -500,6 +504,8 @@ static int cnss_runtime_pm_debug_show(struct seq_file *s, void *data)
seq_puts(s, "put_noidle: do runtime PM put noidle\n");
seq_puts(s, "put_autosuspend: do runtime PM put autosuspend\n");
seq_puts(s, "mark_last_busy: do runtime PM mark last busy\n");
+ seq_puts(s, "resume_bus: do bus resume only\n");
+ seq_puts(s, "suspend_bus: do bus suspend only\n");
return 0;
}
diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c
index f64d31d..5beeaa3 100644
--- a/drivers/net/wireless/cnss2/main.c
+++ b/drivers/net/wireless/cnss2/main.c
@@ -1210,13 +1210,23 @@ EXPORT_SYMBOL(cnss_force_collect_rddm);
int cnss_qmi_send_get(struct device *dev)
{
- return 0;
+ struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
+
+ if (!test_bit(CNSS_QMI_WLFW_CONNECTED, &plat_priv->driver_state))
+ return 0;
+
+ return cnss_bus_qmi_send_get(plat_priv);
}
EXPORT_SYMBOL(cnss_qmi_send_get);
int cnss_qmi_send_put(struct device *dev)
{
- return 0;
+ struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
+
+ if (!test_bit(CNSS_QMI_WLFW_CONNECTED, &plat_priv->driver_state))
+ return 0;
+
+ return cnss_bus_qmi_send_put(plat_priv);
}
EXPORT_SYMBOL(cnss_qmi_send_put);
@@ -1224,7 +1234,25 @@ int cnss_qmi_send(struct device *dev, int type, void *cmd,
int cmd_len, void *cb_ctx,
int (*cb)(void *ctx, void *event, int event_len))
{
- return -EINVAL;
+ struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev);
+ int ret;
+
+ if (!plat_priv)
+ return -ENODEV;
+
+ if (!test_bit(CNSS_QMI_WLFW_CONNECTED, &plat_priv->driver_state))
+ return -EINVAL;
+
+ plat_priv->get_info_cb = cb;
+ plat_priv->get_info_cb_ctx = cb_ctx;
+
+ ret = cnss_wlfw_get_info_send_sync(plat_priv, type, cmd, cmd_len);
+ if (ret) {
+ plat_priv->get_info_cb = NULL;
+ plat_priv->get_info_cb_ctx = NULL;
+ }
+
+ return ret;
}
EXPORT_SYMBOL(cnss_qmi_send);
diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h
index 8009799..9e9068a 100644
--- a/drivers/net/wireless/cnss2/main.h
+++ b/drivers/net/wireless/cnss2/main.h
@@ -353,6 +353,8 @@ struct cnss_plat_data {
struct qmi_handle ims_qmi;
struct qmi_txn txn;
u64 dynamic_feature;
+ void *get_info_cb_ctx;
+ int (*get_info_cb)(void *ctx, void *event, int event_len);
};
#ifdef CONFIG_ARCH_QCOM
diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c
index dcecd94..3b7e5c70 100644
--- a/drivers/net/wireless/cnss2/pci.c
+++ b/drivers/net/wireless/cnss2/pci.c
@@ -1223,6 +1223,9 @@ int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv)
}
}
+ plat_priv->get_info_cb_ctx = NULL;
+ plat_priv->get_info_cb = NULL;
+
return 0;
}
@@ -2015,7 +2018,7 @@ static int cnss_pci_resume_driver(struct cnss_pci_data *pci_priv)
return ret;
}
-static int cnss_pci_suspend_bus(struct cnss_pci_data *pci_priv)
+int cnss_pci_suspend_bus(struct cnss_pci_data *pci_priv)
{
struct pci_dev *pci_dev = pci_priv->pci_dev;
int ret = 0;
@@ -2060,7 +2063,7 @@ static int cnss_pci_suspend_bus(struct cnss_pci_data *pci_priv)
return ret;
}
-static int cnss_pci_resume_bus(struct cnss_pci_data *pci_priv)
+int cnss_pci_resume_bus(struct cnss_pci_data *pci_priv)
{
struct pci_dev *pci_dev = pci_priv->pci_dev;
int ret = 0;
@@ -2486,11 +2489,16 @@ int cnss_auto_suspend(struct device *dev)
if (!plat_priv)
return -ENODEV;
+ mutex_lock(&pci_priv->bus_lock);
ret = cnss_pci_suspend_bus(pci_priv);
- if (ret)
+ if (ret) {
+ mutex_unlock(&pci_priv->bus_lock);
return ret;
+ }
cnss_pci_set_auto_suspended(pci_priv, 1);
+ mutex_unlock(&pci_priv->bus_lock);
+
cnss_pci_set_monitor_wake_intr(pci_priv, true);
bus_bw_info = &plat_priv->bus_bw_info;
@@ -2516,11 +2524,15 @@ int cnss_auto_resume(struct device *dev)
if (!plat_priv)
return -ENODEV;
+ mutex_lock(&pci_priv->bus_lock);
ret = cnss_pci_resume_bus(pci_priv);
- if (ret)
+ if (ret) {
+ mutex_unlock(&pci_priv->bus_lock);
return ret;
+ }
cnss_pci_set_auto_suspended(pci_priv, 0);
+ mutex_unlock(&pci_priv->bus_lock);
bus_bw_info = &plat_priv->bus_bw_info;
msm_bus_scale_client_update_request(bus_bw_info->bus_client,
@@ -2613,6 +2625,46 @@ int cnss_pci_force_wake_release(struct device *dev)
}
EXPORT_SYMBOL(cnss_pci_force_wake_release);
+int cnss_pci_qmi_send_get(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+
+ if (!pci_priv)
+ return -ENODEV;
+
+ mutex_lock(&pci_priv->bus_lock);
+ if (!cnss_pci_get_auto_suspended(pci_priv))
+ goto out;
+
+ cnss_pr_vdbg("Starting to handle get info prepare\n");
+
+ ret = cnss_pci_resume_bus(pci_priv);
+
+out:
+ mutex_unlock(&pci_priv->bus_lock);
+ return ret;
+}
+
+int cnss_pci_qmi_send_put(struct cnss_pci_data *pci_priv)
+{
+ int ret = 0;
+
+ if (!pci_priv)
+ return -ENODEV;
+
+ mutex_lock(&pci_priv->bus_lock);
+ if (!cnss_pci_get_auto_suspended(pci_priv))
+ goto out;
+
+ cnss_pr_vdbg("Starting to handle get info done\n");
+
+ ret = cnss_pci_suspend_bus(pci_priv);
+
+out:
+ mutex_unlock(&pci_priv->bus_lock);
+ return ret;
+}
+
int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv)
{
struct cnss_plat_data *plat_priv = pci_priv->plat_priv;
@@ -3716,6 +3768,7 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
plat_priv->bus_priv = pci_priv;
snprintf(plat_priv->firmware_name, sizeof(plat_priv->firmware_name),
DEFAULT_FW_FILE_NAME);
+ mutex_init(&pci_priv->bus_lock);
ret = cnss_register_subsys(plat_priv);
if (ret)
diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h
index 8dcb14a6..e7da860 100644
--- a/drivers/net/wireless/cnss2/pci.h
+++ b/drivers/net/wireless/cnss2/pci.h
@@ -92,6 +92,7 @@ struct cnss_pci_data {
struct timer_list dev_rddm_timer;
struct delayed_work time_sync_work;
u8 disable_pc;
+ struct mutex bus_lock; /* mutex for suspend and resume bus */
struct cnss_pci_debug_reg *debug_reg;
struct cnss_misc_reg *wcss_reg;
u32 wcss_reg_size;
@@ -173,6 +174,8 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic);
void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv);
u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv);
int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv);
+int cnss_pci_qmi_send_get(struct cnss_pci_data *pci_priv);
+int cnss_pci_qmi_send_put(struct cnss_pci_data *pci_priv);
void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv);
int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv);
int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv);
@@ -196,5 +199,7 @@ void cnss_pci_pm_runtime_mark_last_busy(struct cnss_pci_data *pci_priv);
int cnss_pci_update_status(struct cnss_pci_data *pci_priv,
enum cnss_driver_status status);
int cnss_pcie_is_device_down(struct cnss_pci_data *pci_priv);
+int cnss_pci_suspend_bus(struct cnss_pci_data *pci_priv);
+int cnss_pci_resume_bus(struct cnss_pci_data *pci_priv);
#endif /* _CNSS_PCI_H */
diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c
index 02178b3..2ffb1be 100644
--- a/drivers/net/wireless/cnss2/qmi.c
+++ b/drivers/net/wireless/cnss2/qmi.c
@@ -92,6 +92,8 @@ static int cnss_wlfw_ind_register_send_sync(struct cnss_plat_data *plat_priv)
req->qdss_trace_save_enable = 1;
req->qdss_trace_free_enable_valid = 1;
req->qdss_trace_free_enable = 1;
+ req->respond_get_info_enable_valid = 1;
+ req->respond_get_info_enable = 1;
ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn,
wlfw_ind_register_resp_msg_v01_ei, resp);
@@ -1458,6 +1460,77 @@ int cnss_wlfw_dynamic_feature_mask_send_sync(struct cnss_plat_data *plat_priv)
return ret;
}
+int cnss_wlfw_get_info_send_sync(struct cnss_plat_data *plat_priv, int type,
+ void *cmd, int cmd_len)
+{
+ struct wlfw_get_info_req_msg_v01 *req;
+ struct wlfw_get_info_resp_msg_v01 *resp;
+ struct qmi_txn txn;
+ int ret = 0;
+
+ cnss_pr_vdbg("Sending get info message, type: %d, cmd length: %d, state: 0x%lx\n",
+ type, cmd_len, plat_priv->driver_state);
+
+ if (cmd_len > QMI_WLFW_MAX_DATA_SIZE_V01)
+ return -EINVAL;
+
+ req = kzalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ resp = kzalloc(sizeof(*resp), GFP_KERNEL);
+ if (!resp) {
+ kfree(req);
+ return -ENOMEM;
+ }
+
+ req->type = type;
+ req->data_len = cmd_len;
+ memcpy(req->data, cmd, req->data_len);
+
+ ret = qmi_txn_init(&plat_priv->qmi_wlfw, &txn,
+ wlfw_get_info_resp_msg_v01_ei, resp);
+ if (ret < 0) {
+ cnss_pr_err("Failed to initialize txn for get info request, err: %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_send_request(&plat_priv->qmi_wlfw, NULL, &txn,
+ QMI_WLFW_GET_INFO_REQ_V01,
+ WLFW_GET_INFO_REQ_MSG_V01_MAX_MSG_LEN,
+ wlfw_get_info_req_msg_v01_ei, req);
+ if (ret < 0) {
+ qmi_txn_cancel(&txn);
+ cnss_pr_err("Failed to send get info request, err: %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = qmi_txn_wait(&txn, QMI_WLFW_TIMEOUT_JF);
+ if (ret < 0) {
+ cnss_pr_err("Failed to wait for response of get info request, err: %d\n",
+ ret);
+ goto out;
+ }
+
+ if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
+ cnss_pr_err("Get info request failed, result: %d, err: %d\n",
+ resp->resp.result, resp->resp.error);
+ ret = -resp->resp.result;
+ goto out;
+ }
+
+ kfree(req);
+ kfree(resp);
+ return 0;
+
+out:
+ kfree(req);
+ kfree(resp);
+ return ret;
+}
+
unsigned int cnss_get_qmi_timeout(struct cnss_plat_data *plat_priv)
{
cnss_pr_dbg("QMI timeout is %u ms\n", QMI_WLFW_TIMEOUT_MS);
@@ -1717,6 +1790,31 @@ static void cnss_wlfw_qdss_trace_free_ind_cb(struct qmi_handle *qmi_wlfw,
0, NULL);
}
+static void cnss_wlfw_respond_get_info_ind_cb(struct qmi_handle *qmi_wlfw,
+ struct sockaddr_qrtr *sq,
+ struct qmi_txn *txn,
+ const void *data)
+{
+ struct cnss_plat_data *plat_priv =
+ container_of(qmi_wlfw, struct cnss_plat_data, qmi_wlfw);
+ const struct wlfw_respond_get_info_ind_msg_v01 *ind_msg = data;
+
+ cnss_pr_vdbg("Received QMI WLFW respond get info indication\n");
+
+ if (!txn) {
+ cnss_pr_err("Spurious indication\n");
+ return;
+ }
+
+ cnss_pr_vdbg("Extract message with event length: %d, type: %d, is last: %d, seq no: %d\n",
+ ind_msg->data_len, ind_msg->type,
+ ind_msg->is_last, ind_msg->seq_no);
+
+ if (plat_priv->get_info_cb_ctx && plat_priv->get_info_cb)
+ plat_priv->get_info_cb(plat_priv->get_info_cb_ctx,
+ (void *)ind_msg->data,
+ ind_msg->data_len);
+}
static struct qmi_msg_handler qmi_wlfw_msg_handlers[] = {
{
.type = QMI_INDICATION,
@@ -1785,6 +1883,14 @@ static struct qmi_msg_handler qmi_wlfw_msg_handlers[] = {
sizeof(struct wlfw_qdss_trace_free_ind_msg_v01),
.fn = cnss_wlfw_qdss_trace_free_ind_cb
},
+ {
+ .type = QMI_INDICATION,
+ .msg_id = QMI_WLFW_RESPOND_GET_INFO_IND_V01,
+ .ei = wlfw_respond_get_info_ind_msg_v01_ei,
+ .decoded_size =
+ sizeof(struct wlfw_respond_get_info_ind_msg_v01),
+ .fn = cnss_wlfw_respond_get_info_ind_cb
+ },
{}
};
diff --git a/drivers/net/wireless/cnss2/qmi.h b/drivers/net/wireless/cnss2/qmi.h
index a064660..fc2a2c6 100644
--- a/drivers/net/wireless/cnss2/qmi.h
+++ b/drivers/net/wireless/cnss2/qmi.h
@@ -59,6 +59,8 @@ int cnss_wlfw_ini_send_sync(struct cnss_plat_data *plat_priv,
int cnss_wlfw_antenna_switch_send_sync(struct cnss_plat_data *plat_priv);
int cnss_wlfw_antenna_grant_send_sync(struct cnss_plat_data *plat_priv);
int cnss_wlfw_dynamic_feature_mask_send_sync(struct cnss_plat_data *plat_priv);
+int cnss_wlfw_get_info_send_sync(struct cnss_plat_data *plat_priv, int type,
+ void *cmd, int cmd_len);
int cnss_register_coex_service(struct cnss_plat_data *plat_priv);
void cnss_unregister_coex_service(struct cnss_plat_data *plat_priv);
int coex_antenna_switch_to_wlan_send_sync_msg(struct cnss_plat_data *plat_priv);
diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c
index 03a418e..682a20b 100644
--- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c
+++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c
@@ -704,6 +704,24 @@ struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[] = {
qdss_trace_free_enable),
},
{
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1F,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ respond_get_info_enable_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x1F,
+ .offset = offsetof(struct wlfw_ind_register_req_msg_v01,
+ respond_get_info_enable),
+ },
+ {
.data_type = QMI_EOTI,
.array_type = NO_ARRAY,
.tlv_type = QMI_COMMON_TLV_TYPE,
@@ -3608,3 +3626,143 @@ struct qmi_elem_info wlfw_wfc_call_status_resp_msg_v01_ei[] = {
},
};
+struct qmi_elem_info wlfw_get_info_req_msg_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct wlfw_get_info_req_msg_v01,
+ type),
+ },
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(u16),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct wlfw_get_info_req_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01,
+ .elem_size = sizeof(u8),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct wlfw_get_info_req_msg_v01,
+ data),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct qmi_elem_info wlfw_get_info_resp_msg_v01_ei[] = {
+ {
+ .data_type = QMI_STRUCT,
+ .elem_len = 1,
+ .elem_size = sizeof(struct qmi_response_type_v01),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x02,
+ .offset = offsetof(struct wlfw_get_info_resp_msg_v01,
+ resp),
+ .ei_array = qmi_response_type_v01_ei,
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct qmi_elem_info wlfw_respond_get_info_ind_msg_v01_ei[] = {
+ {
+ .data_type = QMI_DATA_LEN,
+ .elem_len = 1,
+ .elem_size = sizeof(u16),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct
+ wlfw_respond_get_info_ind_msg_v01,
+ data_len),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01,
+ .elem_size = sizeof(u8),
+ .array_type = VAR_LEN_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct
+ wlfw_respond_get_info_ind_msg_v01,
+ data),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct
+ wlfw_respond_get_info_ind_msg_v01,
+ type_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct
+ wlfw_respond_get_info_ind_msg_v01,
+ type),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct
+ wlfw_respond_get_info_ind_msg_v01,
+ is_last_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_1_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct
+ wlfw_respond_get_info_ind_msg_v01,
+ is_last),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(u8),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct
+ wlfw_respond_get_info_ind_msg_v01,
+ seq_no_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(u32),
+ .array_type = NO_ARRAY,
+ .tlv_type = 0x12,
+ .offset = offsetof(struct
+ wlfw_respond_get_info_ind_msg_v01,
+ seq_no),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .array_type = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h
index dacdfdb..0e18d80 100644
--- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h
+++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h
@@ -13,6 +13,7 @@
#define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025
#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037
#define QMI_WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_V01 0x0044
+#define QMI_WLFW_GET_INFO_REQ_V01 0x004A
#define QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01 0x002A
#define QMI_WLFW_CAL_DONE_IND_V01 0x003E
#define QMI_WLFW_WFC_CALL_STATUS_RESP_V01 0x0049
@@ -23,6 +24,7 @@
#define QMI_WLFW_FW_INIT_DONE_IND_V01 0x0038
#define QMI_WLFW_ANTENNA_GRANT_RESP_V01 0x0048
#define QMI_WLFW_CAL_REPORT_REQ_V01 0x0026
+#define QMI_WLFW_RESPOND_GET_INFO_IND_V01 0x004B
#define QMI_WLFW_M3_INFO_RESP_V01 0x003C
#define QMI_WLFW_CAL_UPDATE_RESP_V01 0x0029
#define QMI_WLFW_CAL_DOWNLOAD_RESP_V01 0x0027
@@ -49,6 +51,7 @@
#define QMI_WLFW_ATHDIAG_WRITE_REQ_V01 0x0031
#define QMI_WLFW_WLAN_MODE_RESP_V01 0x0022
#define QMI_WLFW_RESPOND_MEM_REQ_V01 0x0036
+#define QMI_WLFW_GET_INFO_RESP_V01 0x004A
#define QMI_WLFW_QDSS_TRACE_MODE_RESP_V01 0x0045
#define QMI_WLFW_PIN_CONNECT_RESULT_IND_V01 0x002C
#define QMI_WLFW_FW_READY_IND_V01 0x0021
@@ -272,9 +275,11 @@ struct wlfw_ind_register_req_msg_v01 {
u8 qdss_trace_save_enable;
u8 qdss_trace_free_enable_valid;
u8 qdss_trace_free_enable;
+ u8 respond_get_info_enable_valid;
+ u8 respond_get_info_enable;
};
-#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 66
+#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 70
extern struct qmi_elem_info wlfw_ind_register_req_msg_v01_ei[];
struct wlfw_ind_register_resp_msg_v01 {
@@ -949,4 +954,34 @@ struct wlfw_wfc_call_status_resp_msg_v01 {
#define WLFW_WFC_CALL_STATUS_RESP_MSG_V01_MAX_MSG_LEN 7
extern struct qmi_elem_info wlfw_wfc_call_status_resp_msg_v01_ei[];
+struct wlfw_get_info_req_msg_v01 {
+ u8 type;
+ u32 data_len;
+ u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
+};
+
+#define WLFW_GET_INFO_REQ_MSG_V01_MAX_MSG_LEN 6153
+extern struct qmi_elem_info wlfw_get_info_req_msg_v01_ei[];
+
+struct wlfw_get_info_resp_msg_v01 {
+ struct qmi_response_type_v01 resp;
+};
+
+#define WLFW_GET_INFO_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct qmi_elem_info wlfw_get_info_resp_msg_v01_ei[];
+
+struct wlfw_respond_get_info_ind_msg_v01 {
+ u32 data_len;
+ u8 data[QMI_WLFW_MAX_DATA_SIZE_V01];
+ u8 type_valid;
+ u8 type;
+ u8 is_last_valid;
+ u8 is_last;
+ u8 seq_no_valid;
+ u32 seq_no;
+};
+
+#define WLFW_RESPOND_GET_INFO_IND_MSG_V01_MAX_MSG_LEN 6164
+extern struct qmi_elem_info wlfw_respond_get_info_ind_msg_v01_ei[];
+
#endif
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index f06ca58..cd0e278 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -908,6 +908,9 @@ static int pci_pm_resume(struct device *dev)
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
int error = 0;
+ if (pci_dev->no_d3hot)
+ goto skip_pci_pm_restore;
+
/*
* This is necessary for the suspend error path in which resume is
* called without restoring the standard config registers of the device.
@@ -915,6 +918,7 @@ static int pci_pm_resume(struct device *dev)
if (pci_dev->state_saved)
pci_restore_standard_config(pci_dev);
+skip_pci_pm_restore:
if (pci_has_legacy_pm_support(pci_dev))
return pci_legacy_resume(dev);
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index d578a72..cc6bf43 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -2423,6 +2423,11 @@ int gsi_alloc_channel(struct gsi_chan_props *props, unsigned long dev_hdl,
ctx->stats.dp.last_timestamp = jiffies_to_msecs(jiffies);
atomic_inc(&gsi_ctx->num_chan);
+ if (props->prot == GSI_CHAN_PROT_GCI) {
+ gsi_ctx->coal_info.ch_id = props->ch_id;
+ gsi_ctx->coal_info.evchid = props->evt_ring_hdl;
+ }
+
return GSI_STATUS_SUCCESS;
}
EXPORT_SYMBOL(gsi_alloc_channel);
@@ -3155,6 +3160,10 @@ int gsi_dealloc_channel(unsigned long chan_hdl)
atomic_dec(&ctx->evtr->chan_ref_cnt);
atomic_dec(&gsi_ctx->num_chan);
+ if (ctx->props.prot == GSI_CHAN_PROT_GCI) {
+ gsi_ctx->coal_info.ch_id = GSI_CHAN_MAX;
+ gsi_ctx->coal_info.evchid = GSI_EVT_RING_MAX;
+ }
return GSI_STATUS_SUCCESS;
}
EXPORT_SYMBOL(gsi_dealloc_channel);
@@ -3707,7 +3716,7 @@ EXPORT_SYMBOL(gsi_poll_n_channel);
int gsi_config_channel_mode(unsigned long chan_hdl, enum gsi_chan_mode mode)
{
- struct gsi_chan_ctx *ctx;
+ struct gsi_chan_ctx *ctx, *coal_ctx;
enum gsi_chan_mode curr;
unsigned long flags;
enum gsi_chan_mode chan_mode;
@@ -3753,8 +3762,14 @@ int gsi_config_channel_mode(unsigned long chan_hdl, enum gsi_chan_mode mode)
gsi_writel(1 << ctx->evtr->id, gsi_ctx->base +
GSI_EE_n_CNTXT_SRC_IEOB_IRQ_CLR_OFFS(gsi_ctx->per.ee));
atomic_set(&ctx->poll_mode, mode);
- if ((ctx->props.prot == GSI_CHAN_PROT_GCI) && ctx->evtr->chan)
+ if ((ctx->props.prot == GSI_CHAN_PROT_GCI) && ctx->evtr->chan) {
atomic_set(&ctx->evtr->chan->poll_mode, mode);
+ } else if (gsi_ctx->coal_info.evchid == ctx->evtr->id) {
+ coal_ctx = &gsi_ctx->chan[gsi_ctx->coal_info.ch_id];
+ if (coal_ctx != NULL)
+ atomic_set(&coal_ctx->poll_mode, mode);
+ }
+
GSIDBG("set gsi_ctx evtr_id %d to %d mode\n",
ctx->evtr->id, mode);
ctx->stats.callback_to_poll++;
@@ -3763,8 +3778,13 @@ int gsi_config_channel_mode(unsigned long chan_hdl, enum gsi_chan_mode mode)
if (curr == GSI_CHAN_MODE_POLL &&
mode == GSI_CHAN_MODE_CALLBACK) {
atomic_set(&ctx->poll_mode, mode);
- if ((ctx->props.prot == GSI_CHAN_PROT_GCI) && ctx->evtr->chan)
+ if ((ctx->props.prot == GSI_CHAN_PROT_GCI) && ctx->evtr->chan) {
atomic_set(&ctx->evtr->chan->poll_mode, mode);
+ } else if (gsi_ctx->coal_info.evchid == ctx->evtr->id) {
+ coal_ctx = &gsi_ctx->chan[gsi_ctx->coal_info.ch_id];
+ if (coal_ctx != NULL)
+ atomic_set(&coal_ctx->poll_mode, mode);
+ }
__gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, ~0);
GSIDBG("set gsi_ctx evtr_id %d to %d mode\n",
ctx->evtr->id, mode);
diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h
index 45dec44..3b6c648 100644
--- a/drivers/platform/msm/gsi/gsi.h
+++ b/drivers/platform/msm/gsi/gsi.h
@@ -200,6 +200,11 @@ struct gsi_generic_ee_cmd_debug_stats {
unsigned long halt_channel;
};
+struct gsi_coal_chan_info {
+ uint8_t ch_id;
+ uint8_t evchid;
+};
+
struct gsi_ctx {
void __iomem *base;
struct device *dev;
@@ -223,6 +228,7 @@ struct gsi_ctx {
struct completion gen_ee_cmd_compl;
void *ipc_logbuf;
void *ipc_logbuf_low;
+ struct gsi_coal_chan_info coal_info;
/*
* The following used only on emulation systems.
*/
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 927b040..79bc2f9 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -34,6 +34,7 @@
#include <asm/cacheflush.h>
#include <linux/soc/qcom/smem_state.h>
#include <linux/of_irq.h>
+#include <linux/ctype.h>
#ifdef CONFIG_ARM64
@@ -5968,6 +5969,7 @@ static ssize_t ipa3_write(struct file *file, const char __user *buf,
unsigned long missing;
char dbg_buff[32] = { 0 };
+ int i = 0;
if (count >= sizeof(dbg_buff))
return -EFAULT;
@@ -5988,6 +5990,17 @@ static ssize_t ipa3_write(struct file *file, const char __user *buf,
if (ipa3_is_ready())
return count;
+ /*Ignore empty ipa_config file*/
+ for (i = 0 ; i < count ; ++i) {
+ if (!isspace(dbg_buff[i]))
+ break;
+ }
+
+ if (i == count) {
+ IPADBG("Empty ipa_config file\n");
+ return count;
+ }
+
/* Check MHI configuration on MDM devices */
if (ipa3_ctx->platform_type == IPA_PLAT_TYPE_MDM) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index d17ff30..fb8b055 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -2423,7 +2423,7 @@ static const struct ipa_ep_configuration ipa3_ep_mapping
IPA_DPS_HPS_SEQ_TYPE_INVALID,
QMB_MASTER_SELECT_DDR,
{ 16, 10, 9, 9, IPA_EE_AP, GSI_ESCAPE_BUF_ONLY, 0 } },
- [IPA_4_5][IPA_CLIENT_USB_DPL_CONS] = {
+ [IPA_4_5_MHI][IPA_CLIENT_USB_DPL_CONS] = {
true, IPA_v4_5_MHI_GROUP_DDR,
false,
IPA_DPS_HPS_SEQ_TYPE_INVALID,
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index 11c520f..cfc7751 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -3104,7 +3104,8 @@ static int rmnet_ipa3_set_data_quota_wifi(struct wan_ioctl_set_data_quota *data)
IPAWANERR("iface name %s, quota %lu\n",
data->interface_name, (unsigned long) data->quota_mbytes);
- if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_5) {
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_5 &&
+ ipa3_ctx->ipa_hw_type != IPA_HW_v4_7) {
IPADBG("use ipa-uc for quota\n");
rc = ipa3_uc_quota_monitor(data->set_quota);
} else {
@@ -3835,6 +3836,7 @@ int rmnet_ipa3_query_tethering_stats_all(
} else if (upstream_type == IPA_UPSTEAM_WLAN) {
IPAWANDBG_LOW(" query wifi-backhaul stats\n");
if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_5 ||
+ ipa3_ctx->ipa_hw_type == IPA_HW_v4_7 ||
!ipa3_ctx->hw_stats.enabled) {
IPAWANDBG("hw version %d,hw_stats.enabled %d\n",
ipa3_ctx->ipa_hw_type,
diff --git a/drivers/platform/msm/ipa/test/ipa_test_hw_stats.c b/drivers/platform/msm/ipa/test/ipa_test_hw_stats.c
index caae2ba..c4d53df 100644
--- a/drivers/platform/msm/ipa/test/ipa_test_hw_stats.c
+++ b/drivers/platform/msm/ipa/test/ipa_test_hw_stats.c
@@ -736,7 +736,8 @@ static int ipa_test_hw_stats_set_uc_event_ring(void *priv)
/* set uc event ring */
IPA_UT_INFO("========set uc event ring ========\n");
- if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_5) {
+ if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_5 &&
+ ipa3_ctx->ipa_hw_type != IPA_HW_v4_7) {
if (ipa3_ctx->uc_ctx.uc_loaded &&
!ipa3_ctx->uc_ctx.uc_event_ring_valid) {
if (ipa3_uc_setup_event_ring()) {
diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c
index 9a00920..47197f0 100644
--- a/drivers/power/supply/qcom/qpnp-smb5.c
+++ b/drivers/power/supply/qcom/qpnp-smb5.c
@@ -1436,7 +1436,7 @@ static int smb5_dc_get_prop(struct power_supply *psy,
rc = smblib_get_prop_dc_voltage_max(chg, val);
break;
case POWER_SUPPLY_PROP_REAL_TYPE:
- val->intval = POWER_SUPPLY_TYPE_WIPOWER;
+ val->intval = POWER_SUPPLY_TYPE_WIRELESS;
break;
case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION:
rc = smblib_get_prop_voltage_wls_output(chg, val);
@@ -2785,6 +2785,7 @@ static int smb5_determine_initial_status(struct smb5 *chip)
smblib_suspend_on_debug_battery(chg);
usb_plugin_irq_handler(0, &irq_data);
+ dc_plugin_irq_handler(0, &irq_data);
typec_attach_detach_irq_handler(0, &irq_data);
typec_state_change_irq_handler(0, &irq_data);
usb_source_change_irq_handler(0, &irq_data);
diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c
index 2890df6..cadb594 100644
--- a/drivers/power/supply/qcom/smb5-lib.c
+++ b/drivers/power/supply/qcom/smb5-lib.c
@@ -832,10 +832,10 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg,
if (vbus_allowance != CONTINUOUS)
return 0;
+ aicl_threshold = min_allowed_uv / 1000 - CONT_AICL_HEADROOM_MV;
if (chg->adapter_cc_mode)
- aicl_threshold = AICL_THRESHOLD_MV_IN_CC;
- else
- aicl_threshold = min_allowed_uv / 1000 - CONT_AICL_HEADROOM_MV;
+ aicl_threshold = min(aicl_threshold, AICL_THRESHOLD_MV_IN_CC);
+
rc = smblib_set_charge_param(chg, &chg->param.aicl_cont_threshold,
aicl_threshold);
if (rc < 0) {
@@ -3584,6 +3584,7 @@ int smblib_get_prop_typec_power_role(struct smb_charger *chg,
return -EINVAL;
}
+ chg->power_role = val->intval;
return rc;
}
@@ -4172,15 +4173,86 @@ int smblib_set_prop_usb_voltage_max_limit(struct smb_charger *chg,
return 0;
}
+void smblib_typec_irq_config(struct smb_charger *chg, bool en)
+{
+ if (en == chg->typec_irq_en)
+ return;
+
+ if (en) {
+ enable_irq(
+ chg->irq_info[TYPEC_ATTACH_DETACH_IRQ].irq);
+ enable_irq(
+ chg->irq_info[TYPEC_CC_STATE_CHANGE_IRQ].irq);
+ enable_irq(
+ chg->irq_info[TYPEC_OR_RID_DETECTION_CHANGE_IRQ].irq);
+ } else {
+ disable_irq_nosync(
+ chg->irq_info[TYPEC_ATTACH_DETACH_IRQ].irq);
+ disable_irq_nosync(
+ chg->irq_info[TYPEC_CC_STATE_CHANGE_IRQ].irq);
+ disable_irq_nosync(
+ chg->irq_info[TYPEC_OR_RID_DETECTION_CHANGE_IRQ].irq);
+ }
+
+ chg->typec_irq_en = en;
+}
+
+#define PR_LOCK_TIMEOUT_MS 1000
int smblib_set_prop_typec_power_role(struct smb_charger *chg,
const union power_supply_propval *val)
{
int rc = 0;
u8 power_role;
+ enum power_supply_typec_mode typec_mode;
+ bool snk_attached = false, src_attached = false, is_pr_lock = false;
if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
return 0;
+ smblib_dbg(chg, PR_MISC, "power role change: %d --> %d!",
+ chg->power_role, val->intval);
+
+ if (chg->power_role == val->intval) {
+ smblib_dbg(chg, PR_MISC, "power role already in %d, ignore!",
+ chg->power_role);
+ return 0;
+ }
+
+ typec_mode = smblib_get_prop_typec_mode(chg);
+ if (typec_mode >= POWER_SUPPLY_TYPEC_SINK &&
+ typec_mode <= POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER)
+ snk_attached = true;
+ else if (typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT &&
+ typec_mode <= POWER_SUPPLY_TYPEC_SOURCE_HIGH)
+ src_attached = true;
+
+ /*
+ * If current power role is in DRP, and type-c is already in the
+ * mode (source or sink) that's being requested, it means this is
+ * a power role locking request from USBPD driver. Disable type-c
+ * related interrupts for locking power role to avoid the redundant
+ * notifications.
+ */
+ if ((chg->power_role == POWER_SUPPLY_TYPEC_PR_DUAL) &&
+ ((src_attached && val->intval == POWER_SUPPLY_TYPEC_PR_SINK) ||
+ (snk_attached && val->intval == POWER_SUPPLY_TYPEC_PR_SOURCE)))
+ is_pr_lock = true;
+
+ smblib_dbg(chg, PR_MISC, "snk_attached = %d, src_attached = %d, is_pr_lock = %d\n",
+ snk_attached, src_attached, is_pr_lock);
+ cancel_delayed_work_sync(&chg->pr_lock_clear_work);
+ if (!chg->pr_lock_in_progress && is_pr_lock) {
+ smblib_dbg(chg, PR_MISC, "disable type-c interrupts for power role locking\n");
+ smblib_typec_irq_config(chg, false);
+ schedule_delayed_work(&chg->pr_lock_clear_work,
+ msecs_to_jiffies(PR_LOCK_TIMEOUT_MS));
+ } else if (chg->pr_lock_in_progress && !is_pr_lock) {
+ smblib_dbg(chg, PR_MISC, "restore type-c interrupts after exit power role locking\n");
+ smblib_typec_irq_config(chg, true);
+ }
+
+ chg->pr_lock_in_progress = is_pr_lock;
+
switch (val->intval) {
case POWER_SUPPLY_TYPEC_PR_NONE:
power_role = TYPEC_DISABLE_CMD_BIT;
@@ -4208,6 +4280,7 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg,
return rc;
}
+ chg->power_role = val->intval;
return rc;
}
@@ -5532,8 +5605,10 @@ static void typec_src_insertion(struct smb_charger *chg)
int rc = 0;
u8 stat;
- if (chg->pr_swap_in_progress)
+ if (chg->pr_swap_in_progress) {
+ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0);
return;
+ }
rc = smblib_read(chg, LEGACY_CABLE_STATUS_REG, &stat);
if (rc < 0) {
@@ -6190,7 +6265,8 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data)
chg->last_wls_vout = 0;
}
- power_supply_changed(chg->dc_psy);
+ if (chg->dc_psy)
+ power_supply_changed(chg->dc_psy);
smblib_dbg(chg, (PR_WLS | PR_INTERRUPT), "dcin_present= %d, usbin_present= %d, cp_reason = %d\n",
dcin_present, vbus_present, chg->cp_reason);
@@ -6467,13 +6543,19 @@ int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
if (rc < 0) {
smblib_err(chg, "Couldn't read TYPE_C_CCOUT_CONTROL_REG rc=%d\n",
rc);
+ return rc;
}
/* enable DRP */
rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG,
TYPEC_POWER_ROLE_CMD_MASK, 0);
- if (rc < 0)
+ if (rc < 0) {
smblib_err(chg, "Couldn't enable DRP rc=%d\n", rc);
+ return rc;
+ }
+ chg->power_role = POWER_SUPPLY_TYPEC_PR_DUAL;
+ smblib_dbg(chg, PR_MISC, "restore power role: %d\n",
+ chg->power_role);
}
return 0;
@@ -6482,6 +6564,18 @@ int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
/***************
* Work Queues *
***************/
+static void smblib_pr_lock_clear_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ pr_lock_clear_work.work);
+
+ if (chg->pr_lock_in_progress) {
+ smblib_dbg(chg, PR_MISC, "restore type-c interrupts\n");
+ smblib_typec_irq_config(chg, true);
+ chg->pr_lock_in_progress = false;
+ }
+}
+
static void smblib_pr_swap_detach_work(struct work_struct *work)
{
struct smb_charger *chg = container_of(work, struct smb_charger,
@@ -7258,6 +7352,8 @@ int smblib_init(struct smb_charger *chg)
INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work);
INIT_DELAYED_WORK(&chg->pr_swap_detach_work,
smblib_pr_swap_detach_work);
+ INIT_DELAYED_WORK(&chg->pr_lock_clear_work,
+ smblib_pr_lock_clear_work);
if (chg->wa_flags & CHG_TERMINATION_WA) {
INIT_WORK(&chg->chg_termination_work,
@@ -7301,6 +7397,7 @@ int smblib_init(struct smb_charger *chg)
chg->sec_chg_selected = POWER_SUPPLY_CHARGER_SEC_NONE;
chg->cp_reason = POWER_SUPPLY_CP_NONE;
chg->thermal_status = TEMP_BELOW_RANGE;
+ chg->typec_irq_en = true;
switch (chg->mode) {
case PARALLEL_MASTER:
diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h
index 32cd1a6..613304d 100644
--- a/drivers/power/supply/qcom/smb5-lib.h
+++ b/drivers/power/supply/qcom/smb5-lib.h
@@ -447,6 +447,7 @@ struct smb_charger {
struct delayed_work thermal_regulation_work;
struct delayed_work usbov_dbc_work;
struct delayed_work pr_swap_detach_work;
+ struct delayed_work pr_lock_clear_work;
struct alarm lpd_recheck_timer;
struct alarm moisture_protection_alarm;
@@ -464,10 +465,12 @@ struct smb_charger {
int voltage_max_uv;
int pd_active;
bool pd_hard_reset;
+ bool pr_lock_in_progress;
bool pr_swap_in_progress;
bool early_usb_attach;
bool ok_to_pd;
bool typec_legacy;
+ bool typec_irq_en;
/* cached status */
bool system_suspend_supported;
@@ -502,6 +505,7 @@ struct smb_charger {
int hw_max_icl_ua;
int auto_recharge_soc;
enum sink_src_mode sink_src_mode;
+ enum power_supply_typec_power_role power_role;
enum jeita_cfg_stat jeita_configured;
int charger_temp_max;
int smb_temp_max;
diff --git a/drivers/regulator/qpnp-amoled-regulator.c b/drivers/regulator/qpnp-amoled-regulator.c
index f5f89ee..78da900 100644
--- a/drivers/regulator/qpnp-amoled-regulator.c
+++ b/drivers/regulator/qpnp-amoled-regulator.c
@@ -15,7 +15,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
@@ -74,7 +73,6 @@ struct ab_regulator {
/* DT params */
bool swire_control;
bool pd_control;
- u32 aod_entry_poll_time_ms;
};
struct ibb_regulator {
@@ -91,9 +89,6 @@ struct qpnp_amoled {
struct oledb_regulator oledb;
struct ab_regulator ab;
struct ibb_regulator ibb;
- struct mutex reg_lock;
- struct work_struct aod_work;
- struct workqueue_struct *wq;
/* DT params */
u32 oledb_base;
@@ -226,102 +221,6 @@ static int qpnp_ab_pd_control(struct qpnp_amoled *chip, bool en)
return qpnp_amoled_write(chip, AB_LDO_PD_CTL(chip), &val, 1);
}
-#define AB_VREG_OK_POLL_TIME_US 2000
-#define AB_VREG_OK_POLL_HIGH_TRIES 8
-#define AB_VREG_OK_POLL_HIGH_TIME_US 10000
-#define AB_VREG_OK_POLL_AGAIN_TRIES 10
-
-static int qpnp_ab_poll_vreg_ok(struct qpnp_amoled *chip, bool status,
- u32 poll_time_us)
-{
- u32 i, poll_us = AB_VREG_OK_POLL_TIME_US, wait_time_us = 0;
- bool swire_high = false, poll_again = false, monitor = false;
- int rc;
- u8 val;
-
- if (poll_time_us < AB_VREG_OK_POLL_TIME_US)
- return -EINVAL;
-
- i = poll_time_us / AB_VREG_OK_POLL_TIME_US;
-loop:
- while (i--) {
- /* Write a dummy value before reading AB_STATUS1 */
- rc = qpnp_amoled_write(chip, AB_STATUS1(chip), &val, 1);
- if (rc < 0)
- return rc;
-
- rc = qpnp_amoled_read(chip, AB_STATUS1(chip), &val, 1);
- if (rc < 0)
- return rc;
-
- wait_time_us += poll_us;
- if (((val & VREG_OK_BIT) >> VREG_OK_SHIFT) == status) {
- pr_debug("Waited for %d us\n", wait_time_us);
-
- /*
- * Return if we're polling for VREG_OK low. Else, poll
- * for VREG_OK high for at least 80 ms. IF VREG_OK stays
- * high, then consider it as a valid SWIRE pulse.
- */
-
- if (status) {
- swire_high = true;
- if (!poll_again && !monitor) {
- pr_debug("SWIRE is high, start monitoring\n");
- i = AB_VREG_OK_POLL_HIGH_TRIES;
- poll_us = AB_VREG_OK_POLL_HIGH_TIME_US;
- wait_time_us = 0;
- monitor = true;
- }
-
- if (poll_again)
- poll_again = false;
- } else {
- return 0;
- }
- } else {
- /*
- * If we're here when polling for VREG_OK high, then it
- * is possibly because of an intermittent SWIRE pulse.
- * Ignore it and poll for valid SWIRE pulse again.
- */
- if (status && swire_high && monitor) {
- pr_debug("SWIRE is low\n");
- poll_again = true;
- swire_high = false;
- break;
- }
-
- if (poll_again)
- poll_again = false;
- }
-
- usleep_range(poll_us, poll_us + 1);
- }
-
- /*
- * If poll_again is set, then VREG_OK should be polled for another
- * 100 ms for valid SWIRE signal.
- */
-
- if (poll_again) {
- pr_debug("polling again for SWIRE\n");
- i = AB_VREG_OK_POLL_AGAIN_TRIES;
- poll_us = AB_VREG_OK_POLL_HIGH_TIME_US;
- wait_time_us = 0;
- goto loop;
- }
-
- /* If swire_high is set, then it's a valid SWIRE signal, return 0. */
- if (swire_high) {
- pr_debug("SWIRE is high\n");
- return 0;
- }
-
- pr_err("AB_STATUS1: %x poll for VREG_OK %d timed out\n", val, status);
- return -ETIMEDOUT;
-}
-
static int qpnp_ibb_pd_control(struct qpnp_amoled *chip, bool en)
{
u8 val = en ? ENABLE_PD_BIT : 0;
@@ -330,70 +229,24 @@ static int qpnp_ibb_pd_control(struct qpnp_amoled *chip, bool en)
val);
}
-static int qpnp_ibb_aod_config(struct qpnp_amoled *chip, bool aod)
+static int qpnp_ab_ibb_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
{
+ struct qpnp_amoled *chip = rdev_get_drvdata(rdev);
int rc;
- u8 ps_ctl, smart_ps_ctl, nlimit_dac;
- pr_debug("aod: %d\n", aod);
- if (aod) {
- ps_ctl = 0x82;
- smart_ps_ctl = 0;
- nlimit_dac = 0;
- } else {
- ps_ctl = 0x02;
- smart_ps_ctl = 0x80;
- nlimit_dac = 0x3;
+ if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_STANDBY &&
+ mode != REGULATOR_MODE_IDLE) {
+ pr_err("Unsupported mode %u\n", mode);
+ return -EINVAL;
}
- rc = qpnp_amoled_write(chip, IBB_SMART_PS_CTL(chip), &smart_ps_ctl, 1);
- if (rc < 0)
- return rc;
-
- rc = qpnp_amoled_write(chip, IBB_NLIMIT_DAC(chip), &nlimit_dac, 1);
- if (rc < 0)
- return rc;
-
- rc = qpnp_amoled_write(chip, IBB_PS_CTL(chip), &ps_ctl, 1);
- return rc;
-}
-
-static void qpnp_amoled_aod_work(struct work_struct *work)
-{
- struct qpnp_amoled *chip = container_of(work, struct qpnp_amoled,
- aod_work);
- u8 val = 0;
- unsigned int mode;
- u32 poll_time_us = 100000;
- int rc;
-
- mutex_lock(&chip->reg_lock);
- mode = chip->ab.vreg.mode;
- mutex_unlock(&chip->reg_lock);
+ if (mode == chip->ab.vreg.mode || mode == chip->ibb.vreg.mode)
+ return 0;
pr_debug("mode: %d\n", mode);
- if (mode == REGULATOR_MODE_NORMAL) {
- rc = qpnp_ibb_aod_config(chip, true);
- if (rc < 0)
- goto error;
- /* poll for VREG_OK high */
- rc = qpnp_ab_poll_vreg_ok(chip, true, poll_time_us);
- if (rc < 0)
- goto error;
-
- /*
- * As per the hardware recommendation, Wait for ~10 ms after
- * polling for VREG_OK before changing the configuration when
- * exiting from AOD mode.
- */
-
- usleep_range(10000, 10001);
-
- rc = qpnp_ibb_aod_config(chip, false);
- if (rc < 0)
- goto error;
-
+ if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_STANDBY) {
if (chip->ibb.pd_control) {
rc = qpnp_ibb_pd_control(chip, true);
if (rc < 0)
@@ -406,14 +259,6 @@ static void qpnp_amoled_aod_work(struct work_struct *work)
goto error;
}
} else if (mode == REGULATOR_MODE_IDLE) {
- if (chip->ab.aod_entry_poll_time_ms > 0)
- poll_time_us = chip->ab.aod_entry_poll_time_ms * 1000;
-
- /* poll for VREG_OK low */
- rc = qpnp_ab_poll_vreg_ok(chip, false, poll_time_us);
- if (rc < 0)
- goto error;
-
if (chip->ibb.pd_control) {
rc = qpnp_ibb_pd_control(chip, false);
if (rc < 0)
@@ -425,53 +270,12 @@ static void qpnp_amoled_aod_work(struct work_struct *work)
if (rc < 0)
goto error;
}
-
- val = 0xF1;
- } else if (mode == REGULATOR_MODE_STANDBY) {
- /* Restore the normal configuration without any delay */
- rc = qpnp_ibb_aod_config(chip, false);
- if (rc < 0)
- goto error;
-
- if (chip->ibb.pd_control) {
- rc = qpnp_ibb_pd_control(chip, true);
- if (rc < 0)
- goto error;
- }
-
- if (chip->ab.pd_control) {
- rc = qpnp_ab_pd_control(chip, true);
- if (rc < 0)
- goto error;
- }
}
- rc = qpnp_amoled_write(chip, AB_LDO_SW_DBG_CTL(chip), &val, 1);
+ chip->ab.vreg.mode = chip->ibb.vreg.mode = mode;
error:
if (rc < 0)
pr_err("Failed to configure for mode %d\n", mode);
-}
-
-static int qpnp_ab_ibb_regulator_set_mode(struct regulator_dev *rdev,
- unsigned int mode)
-{
- struct qpnp_amoled *chip = rdev_get_drvdata(rdev);
-
- if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_STANDBY &&
- mode != REGULATOR_MODE_IDLE) {
- pr_err("Unsupported mode %u\n", mode);
- return -EINVAL;
- }
-
- pr_debug("mode: %d\n", mode);
- if (mode == chip->ab.vreg.mode || mode == chip->ibb.vreg.mode)
- return 0;
-
- mutex_lock(&chip->reg_lock);
- chip->ab.vreg.mode = chip->ibb.vreg.mode = mode;
- mutex_unlock(&chip->reg_lock);
-
- queue_work(chip->wq, &chip->aod_work);
return 0;
}
@@ -720,9 +524,6 @@ static int qpnp_amoled_parse_dt(struct qpnp_amoled *chip)
"qcom,swire-control");
chip->ab.pd_control = of_property_read_bool(temp,
"qcom,aod-pd-control");
- of_property_read_u32(temp,
- "qcom,aod-entry-poll-time-ms",
- &chip->ab.aod_entry_poll_time_ms);
break;
case IBB_PERIPH_TYPE:
chip->ibb_base = base;
@@ -758,19 +559,6 @@ static int qpnp_amoled_regulator_probe(struct platform_device *pdev)
if (!chip)
return -ENOMEM;
- /*
- * We need this workqueue to order the mode transitions that require
- * timing considerations. This way, we can ensure whenever the mode
- * transition is requested, it can be queued with high priority.
- */
- chip->wq = alloc_ordered_workqueue("qpnp_amoled_wq", WQ_HIGHPRI);
- if (!chip->wq) {
- dev_err(chip->dev, "Unable to alloc workqueue\n");
- return -ENOMEM;
- }
-
- mutex_init(&chip->reg_lock);
- INIT_WORK(&chip->aod_work, qpnp_amoled_aod_work);
chip->dev = &pdev->dev;
chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
@@ -789,23 +577,15 @@ static int qpnp_amoled_regulator_probe(struct platform_device *pdev)
}
rc = qpnp_amoled_hw_init(chip);
- if (rc < 0) {
+ if (rc < 0)
dev_err(chip->dev, "Failed to initialize HW rc=%d\n", rc);
- goto error;
- }
- return 0;
error:
- destroy_workqueue(chip->wq);
return rc;
}
static int qpnp_amoled_regulator_remove(struct platform_device *pdev)
{
- struct qpnp_amoled *chip = dev_get_drvdata(&pdev->dev);
-
- cancel_work_sync(&chip->aod_work);
- destroy_workqueue(chip->wq);
return 0;
}
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 4da79b9..2da1b15 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -535,6 +535,7 @@ struct ufs_vreg {
int max_uV;
bool low_voltage_sup;
bool low_voltage_active;
+ bool sys_suspend_pwr_off;
int min_uA;
int max_uA;
};
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 382e923..bd0415c 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -217,9 +217,24 @@ static int ufshcd_populate_vreg(struct device *dev, const char *name,
} else if (!strcmp(name, "vccq")) {
vreg->min_uV = UFS_VREG_VCCQ_MIN_UV;
vreg->max_uV = UFS_VREG_VCCQ_MAX_UV;
+ /**
+ * Only if the SoC supports turning off VCCQ or VCCQ2 power
+ * supply source during power collapse, set a flag to turn off
+ * the specified power supply to reduce the system power
+ * consumption during system suspend events. The tradeoffs are:
+ * - System resume time will increase due
+ * to UFS device full re-initialization time.
+ * - UFS device life may be affected due to multiple
+ * UFS power on/off events.
+ * The benefits vs tradeoff should be considered carefully.
+ */
+ if (of_property_read_bool(np, "vccq-pwr-collapse-sup"))
+ vreg->sys_suspend_pwr_off = true;
} else if (!strcmp(name, "vccq2")) {
vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV;
vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV;
+ if (of_property_read_bool(np, "vccq2-pwr-collapse-sup"))
+ vreg->sys_suspend_pwr_off = true;
}
goto out;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 3bbff74..e4329a45 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -8864,6 +8864,22 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
goto out;
}
+ /**
+ * UFS3.0 and newer devices use Vcc and Vccq(1.2V)
+ * while UFS2.1 devices use Vcc and Vccq2(1.8V) power
+ * supplies. If the system allows turning off the regulators
+ * during power collapse event, turn off the regulators
+ * during system suspend events. This will cause the UFS
+ * device to re-initialize upon system resume events.
+ */
+ if ((hba->dev_info.w_spec_version >= 0x300 &&
+ hba->vreg_info.vccq->sys_suspend_pwr_off) ||
+ (hba->dev_info.w_spec_version < 0x300 &&
+ hba->vreg_info.vccq2->sys_suspend_pwr_off))
+ hba->spm_lvl = ufs_get_desired_pm_lvl_for_dev_link_state(
+ UFS_POWERDOWN_PWR_MODE,
+ UIC_LINK_OFF_STATE);
+
/* UFS device is also active now */
ufshcd_set_ufs_dev_active(hba);
ufshcd_force_reset_auto_bkops(hba);
@@ -10109,7 +10125,20 @@ static void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
*/
if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba) &&
!hba->dev_info.is_lu_power_on_wp) {
- ufshcd_setup_vreg(hba, false);
+ ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
+ if (hba->dev_info.w_spec_version >= 0x300 &&
+ hba->vreg_info.vccq->sys_suspend_pwr_off)
+ ufshcd_toggle_vreg(hba->dev,
+ hba->vreg_info.vccq, false);
+ else
+ ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
+
+ if (hba->dev_info.w_spec_version < 0x300 &&
+ hba->vreg_info.vccq2->sys_suspend_pwr_off)
+ ufshcd_toggle_vreg(hba->dev,
+ hba->vreg_info.vccq2, false);
+ else
+ ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq2);
} else if (!ufshcd_is_ufs_dev_active(hba)) {
ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
if (!ufshcd_is_link_active(hba)) {
@@ -10124,23 +10153,40 @@ static int ufshcd_vreg_set_hpm(struct ufs_hba *hba)
int ret = 0;
if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba) &&
- !hba->dev_info.is_lu_power_on_wp) {
- ret = ufshcd_setup_vreg(hba, true);
- } else if (!ufshcd_is_ufs_dev_active(hba)) {
- if (!ret && !ufshcd_is_link_active(hba)) {
+ !hba->dev_info.is_lu_power_on_wp) {
+ if (hba->dev_info.w_spec_version < 0x300 &&
+ hba->vreg_info.vccq2->sys_suspend_pwr_off)
+ ret = ufshcd_toggle_vreg(hba->dev,
+ hba->vreg_info.vccq2, true);
+ else
+ ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq2);
+ if (ret)
+ goto vcc_disable;
+
+ if (hba->dev_info.w_spec_version >= 0x300 &&
+ hba->vreg_info.vccq->sys_suspend_pwr_off)
+ ret = ufshcd_toggle_vreg(hba->dev,
+ hba->vreg_info.vccq, true);
+ else
ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq);
- if (ret)
- goto vcc_disable;
+ if (ret)
+ goto vccq2_lpm;
+ ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true);
+ } else if (!ufshcd_is_ufs_dev_active(hba)) {
+ if (!ufshcd_is_link_active(hba)) {
ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq2);
if (ret)
- goto vccq_lpm;
+ goto vcc_disable;
+ ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq);
+ if (ret)
+ goto vccq2_lpm;
}
ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true);
}
goto out;
-vccq_lpm:
- ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
+vccq2_lpm:
+ ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq2);
vcc_disable:
ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
out:
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index 9007fa3..724d597 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -379,7 +379,11 @@ static int pil_do_minidump(struct pil_desc *desc, void *ramdump_dev)
&ss_valid_seg_cnt,
desc->num_aux_minidump_ids);
- ret = do_minidump(ramdump_dev, ramdump_segs, ss_valid_seg_cnt);
+ if (desc->minidump_as_elf32)
+ ret = do_elf_ramdump(ramdump_dev, ramdump_segs,
+ ss_valid_seg_cnt);
+ else
+ ret = do_minidump(ramdump_dev, ramdump_segs, ss_valid_seg_cnt);
if (ret)
pil_err(desc, "%s: Minidump collection failed for subsys %s rc:%d\n",
__func__, desc->name, ret);
@@ -1557,6 +1561,9 @@ int pil_desc_init(struct pil_desc *desc)
if (!desc->unmap_fw_mem)
desc->unmap_fw_mem = unmap_fw_mem;
+ desc->minidump_as_elf32 = of_property_read_bool(
+ ofnode, "qcom,minidump-as-elf32");
+
return 0;
err_parse_dt:
ida_simple_remove(&pil_ida, priv->id);
diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h
index c265657..62e5d4a 100644
--- a/drivers/soc/qcom/peripheral-loader.h
+++ b/drivers/soc/qcom/peripheral-loader.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (c) 2010-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010-2019, The Linux Foundation. All rights reserved.
*/
#ifndef __MSM_PERIPHERAL_LOADER_H
#define __MSM_PERIPHERAL_LOADER_H
@@ -62,6 +62,7 @@ struct pil_desc {
int minidump_id;
int *aux_minidump_ids;
int num_aux_minidump_ids;
+ bool minidump_as_elf32;
};
/**
diff --git a/drivers/soc/qcom/qmi_rmnet.c b/drivers/soc/qcom/qmi_rmnet.c
index 46732bf..5cc51bf 100644
--- a/drivers/soc/qcom/qmi_rmnet.c
+++ b/drivers/soc/qcom/qmi_rmnet.c
@@ -976,6 +976,9 @@ void qmi_rmnet_work_init(void *port)
rmnet_ps_wq = alloc_workqueue("rmnet_powersave_work",
WQ_MEM_RECLAIM | WQ_CPU_INTENSIVE, 1);
+ if (!rmnet_ps_wq)
+ return;
+
rmnet_work = kzalloc(sizeof(*rmnet_work), GFP_ATOMIC);
if (!rmnet_work) {
destroy_workqueue(rmnet_ps_wq);
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index 4443e277..be34be0 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
*/
#include <linux/atomic.h>
@@ -559,6 +559,10 @@ int rpmh_flush(const struct device *dev)
return 0;
}
+ do {
+ ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr));
+ } while (ret == -EAGAIN);
+
/* First flush the cached batch requests */
ret = flush_batch(ctrlr);
if (ret)
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index 233346b..2ee761a 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -185,6 +185,12 @@ struct spcom_channel {
size_t actual_rx_size; /* actual data size received */
void *rpmsg_rx_buf;
+ /**
+ * to track if rx_buf is read in the same session
+ * in which it is updated
+ */
+ uint32_t rx_buf_txn_id;
+
/* shared buffer lock/unlock support */
int dmabuf_fd_table[SPCOM_MAX_ION_BUF_PER_CH];
struct dma_buf *dmabuf_handle_table[SPCOM_MAX_ION_BUF_PER_CH];
@@ -341,6 +347,7 @@ static int spcom_init_channel(struct spcom_channel *ch,
ch->actual_rx_size = 0;
ch->is_busy = false;
ch->txn_id = INITIAL_TXN_ID; /* use non-zero nonce for debug */
+ ch->rx_buf_txn_id = ch->txn_id;
memset(ch->pid, 0, sizeof(ch->pid));
ch->rpmsg_abort = false;
ch->rpmsg_rx_buf = NULL;
@@ -394,6 +401,16 @@ static int spcom_rx(struct spcom_channel *ch,
mutex_lock(&ch->lock);
+ if (ch->rx_buf_txn_id != ch->txn_id) {
+ pr_debug("rpmsg_rx_buf is updated in a different session\n");
+ if (ch->rpmsg_rx_buf) {
+ memset(ch->rpmsg_rx_buf, 0, ch->actual_rx_size);
+ kfree((void *)ch->rpmsg_rx_buf);
+ ch->rpmsg_rx_buf = NULL;
+ ch->actual_rx_size = 0;
+ }
+ }
+
/* check for already pending data */
if (!ch->actual_rx_size) {
reinit_completion(&ch->rx_done);
@@ -2161,6 +2178,7 @@ static void spcom_signal_rx_done(struct work_struct *ignored)
}
ch->rpmsg_rx_buf = rx_item->rpmsg_rx_buf;
ch->actual_rx_size = rx_item->rx_buf_size;
+ ch->rx_buf_txn_id = ch->txn_id;
complete_all(&ch->rx_done);
mutex_unlock(&ch->lock);
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index a57cf1d..1c8e898 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -166,6 +166,7 @@ struct msm_geni_serial_port {
void *ipc_log_rx;
void *ipc_log_pwr;
void *ipc_log_misc;
+ void *console_log;
unsigned int cur_baud;
int ioctl_count;
int edge_count;
@@ -777,6 +778,7 @@ static void msm_geni_serial_console_write(struct console *co, const char *s,
bool locked = true;
unsigned long flags;
unsigned int geni_status;
+ int irq_en;
/* Max 1 port supported as of now */
WARN_ON(co->index < 0 || co->index >= GENI_UART_CONS_PORTS);
@@ -806,12 +808,22 @@ static void msm_geni_serial_console_write(struct console *co, const char *s,
}
writel_relaxed(M_CMD_CANCEL_EN, uport->membase +
SE_GENI_M_IRQ_CLEAR);
- } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->cur_tx_remaining)
+ } else if ((geni_status & M_GENI_CMD_ACTIVE) &&
+ !port->cur_tx_remaining) {
/* It seems we can interrupt existing transfers unless all data
* has been sent, in which case we need to look for done first.
*/
msm_geni_serial_poll_cancel_tx(uport);
+ /* Enable WM interrupt for every new console write op */
+ if (uart_circ_chars_pending(&uport->state->xmit)) {
+ irq_en = geni_read_reg_nolog(uport->membase,
+ SE_GENI_M_IRQ_EN);
+ geni_write_reg_nolog(irq_en | M_TX_FIFO_WATERMARK_EN,
+ uport->membase, SE_GENI_M_IRQ_EN);
+ }
+ }
+
__msm_geni_serial_console_write(uport, s, count);
if (port->cur_tx_remaining)
@@ -1310,6 +1322,7 @@ static int msm_geni_serial_handle_tx(struct uart_port *uport, bool done,
unsigned int fifo_width_bytes =
(uart_console(uport) ? 1 : (msm_port->tx_fifo_width >> 3));
int temp_tail = 0;
+ int irq_en;
tx_fifo_status = geni_read_reg_nolog(uport->membase,
SE_GENI_TX_FIFO_STATUS);
@@ -1340,6 +1353,12 @@ static int msm_geni_serial_handle_tx(struct uart_port *uport, bool done,
if (!msm_port->cur_tx_remaining) {
msm_geni_serial_setup_tx(uport, pending);
msm_port->cur_tx_remaining = pending;
+
+ /* Re-enable WM interrupt when starting new transfer */
+ irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
+ if (!(irq_en & M_TX_FIFO_WATERMARK_EN))
+ geni_write_reg_nolog(irq_en | M_TX_FIFO_WATERMARK_EN,
+ uport->membase, SE_GENI_M_IRQ_EN);
}
bytes_remaining = xmit_size;
@@ -1367,7 +1386,24 @@ static int msm_geni_serial_handle_tx(struct uart_port *uport, bool done,
wmb();
}
xmit->tail = temp_tail;
+
+ /*
+ * The tx fifo watermark is level triggered and latched. Though we had
+ * cleared it in qcom_geni_serial_isr it will have already reasserted
+ * so we must clear it again here after our writes.
+ */
+ geni_write_reg_nolog(M_TX_FIFO_WATERMARK_EN, uport->membase,
+ SE_GENI_M_IRQ_CLEAR);
+
exit_handle_tx:
+ if (!msm_port->cur_tx_remaining) {
+ irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
+ /* Clear WM interrupt post each transfer completion */
+ if (irq_en & M_TX_FIFO_WATERMARK_EN)
+ geni_write_reg_nolog(irq_en & ~M_TX_FIFO_WATERMARK_EN,
+ uport->membase, SE_GENI_M_IRQ_EN);
+ }
+
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(uport);
return 0;
@@ -1469,8 +1505,11 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev)
bool drop_rx = false;
spin_lock_irqsave(&uport->lock, flags);
- if (uart_console(uport) && uport->suspended)
+ if (uart_console(uport) && uport->suspended) {
+ IPC_LOG_MSG(msm_port->console_log,
+ "%s. Console in suspend state\n", __func__);
goto exit_geni_serial_isr;
+ }
if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
IPC_LOG_MSG(msm_port->ipc_log_misc,
@@ -1481,6 +1520,10 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev)
SE_GENI_M_IRQ_STATUS);
s_irq_status = geni_read_reg_nolog(uport->membase,
SE_GENI_S_IRQ_STATUS);
+ if (uart_console(uport))
+ IPC_LOG_MSG(msm_port->console_log,
+ "%s. sirq 0x%x mirq:0x%x\n", __func__, s_irq_status,
+ m_irq_status);
m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN);
dma = geni_read_reg_nolog(uport->membase, SE_GENI_DMA_MODE_EN);
dma_tx_status = geni_read_reg_nolog(uport->membase, SE_DMA_TX_IRQ_STAT);
@@ -2318,14 +2361,13 @@ static void console_unregister(struct uart_driver *drv)
static void msm_geni_serial_debug_init(struct uart_port *uport, bool console)
{
struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
+ char name[30];
msm_port->dbg = debugfs_create_dir(dev_name(uport->dev), NULL);
if (IS_ERR_OR_NULL(msm_port->dbg))
dev_err(uport->dev, "Failed to create dbg dir\n");
if (!console) {
- char name[30];
-
memset(name, 0, sizeof(name));
if (!msm_port->ipc_log_rx) {
scnprintf(name, sizeof(name), "%s%s",
@@ -2362,6 +2404,16 @@ static void msm_geni_serial_debug_init(struct uart_port *uport, bool console)
if (!msm_port->ipc_log_misc)
dev_info(uport->dev, "Err in Misc IPC Log\n");
}
+ } else {
+ memset(name, 0, sizeof(name));
+ if (!msm_port->console_log) {
+ scnprintf(name, sizeof(name), "%s%s",
+ dev_name(uport->dev), "_console");
+ msm_port->console_log = ipc_log_context_create(
+ IPC_LOG_MISC_PAGES, name, 0);
+ if (!msm_port->console_log)
+ dev_info(uport->dev, "Err in Misc IPC Log\n");
+ }
}
}
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 3e79dba..8a0f116 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1406,6 +1406,7 @@ static int dwc3_probe(struct platform_device *pdev)
void __iomem *regs;
int irq;
+ char dma_ipc_log_ctx_name[40];
if (count >= DWC_CTRL_COUNT) {
dev_err(dev, "Err dwc instance %d >= %d available\n",
@@ -1545,6 +1546,13 @@ static int dwc3_probe(struct platform_device *pdev)
if (!dwc->dwc_ipc_log_ctxt)
dev_err(dwc->dev, "Error getting ipc_log_ctxt\n");
+ snprintf(dma_ipc_log_ctx_name, sizeof(dma_ipc_log_ctx_name),
+ "%s.ep_events", dev_name(dwc->dev));
+ dwc->dwc_dma_ipc_log_ctxt = ipc_log_context_create(NUM_LOG_PAGES,
+ dma_ipc_log_ctx_name, 0);
+ if (!dwc->dwc_dma_ipc_log_ctxt)
+ dev_err(dwc->dev, "Error getting ipc_log_ctxt for ep_events\n");
+
dwc3_instance[count] = dwc;
dwc->index = count;
count++;
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6f24144..4b039b3 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -1318,6 +1318,7 @@ struct dwc3 {
unsigned int index;
void *dwc_ipc_log_ctxt;
+ void *dwc_dma_ipc_log_ctxt;
struct dwc3_gadget_events dbg_gadget_events;
int tx_fifo_size;
int last_fifo_depth;
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index a03602d..f1659b7 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -36,6 +36,18 @@
#define dbg_setup(ep_num, req) \
dwc3_dbg_setup(dwc, ep_num, req)
+#define dbg_ep_queue(ep_num, req) \
+ dwc3_dbg_dma_queue(dwc, ep_num, req)
+
+#define dbg_ep_dequeue(ep_num, req) \
+ dwc3_dbg_dma_dequeue(dwc, ep_num, req)
+
+#define dbg_ep_unmap(ep_num, req) \
+ dwc3_dbg_dma_unmap(dwc, ep_num, req)
+
+#define dbg_ep_map(ep_num, req) \
+ dwc3_dbg_dma_map(dwc, ep_num, req)
+
#define dbg_log_string(fmt, ...) \
ipc_log_string(dwc->dwc_ipc_log_ctxt,\
"%s: " fmt, __func__, ##__VA_ARGS__)
@@ -660,6 +672,14 @@ void dwc3_dbg_setup(struct dwc3 *dwc, u8 ep_num,
const struct usb_ctrlrequest *req);
void dwc3_dbg_print_reg(struct dwc3 *dwc,
const char *name, int reg);
+void dwc3_dbg_dma_queue(struct dwc3 *dwc, u8 ep_num,
+ struct dwc3_request *req);
+void dwc3_dbg_dma_dequeue(struct dwc3 *dwc, u8 ep_num,
+ struct dwc3_request *req);
+void dwc3_dbg_dma_map(struct dwc3 *dwc, u8 ep_num,
+ struct dwc3_request *req);
+void dwc3_dbg_dma_unmap(struct dwc3 *dwc, u8 ep_num,
+ struct dwc3_request *req);
#ifdef CONFIG_DEBUG_FS
extern void dwc3_debugfs_init(struct dwc3 *);
diff --git a/drivers/usb/dwc3/debug_ipc.c b/drivers/usb/dwc3/debug_ipc.c
index b694246..98f27a5 100644
--- a/drivers/usb/dwc3/debug_ipc.c
+++ b/drivers/usb/dwc3/debug_ipc.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
*/
#include "debug.h"
@@ -136,3 +136,47 @@ void dwc3_dbg_print_reg(struct dwc3 *dwc, const char *name, int reg)
ipc_log_string(dwc->dwc_ipc_log_ctxt, "%s = 0x%08x", name, reg);
}
+
+void dwc3_dbg_dma_unmap(struct dwc3 *dwc, u8 ep_num, struct dwc3_request *req)
+{
+ if (ep_num < 2)
+ return;
+
+ ipc_log_string(dwc->dwc_dma_ipc_log_ctxt,
+ "%02X-%-3.3s %-25.25s 0x%pK 0x%lx %u 0x%lx %d", ep_num >> 1,
+ ep_num & 1 ? "IN":"OUT", "UNMAP", &req->request,
+ req->request.dma, req->request.length, req->trb_dma,
+ req->trb->ctrl & DWC3_TRB_CTRL_HWO);
+}
+
+void dwc3_dbg_dma_map(struct dwc3 *dwc, u8 ep_num, struct dwc3_request *req)
+{
+ if (ep_num < 2)
+ return;
+
+ ipc_log_string(dwc->dwc_dma_ipc_log_ctxt,
+ "%02X-%-3.3s %-25.25s 0x%pK 0x%lx %u 0x%lx", ep_num >> 1,
+ ep_num & 1 ? "IN":"OUT", "MAP", &req->request, req->request.dma,
+ req->request.length, req->trb_dma);
+}
+
+void dwc3_dbg_dma_dequeue(struct dwc3 *dwc, u8 ep_num, struct dwc3_request *req)
+{
+ if (ep_num < 2)
+ return;
+
+ ipc_log_string(dwc->dwc_dma_ipc_log_ctxt,
+ "%02X-%-3.3s %-25.25s 0x%pK 0x%lx 0x%lx", ep_num >> 1,
+ ep_num & 1 ? "IN":"OUT", "DEQUEUE", &req->request,
+ req->request.dma, req->trb_dma);
+}
+
+void dwc3_dbg_dma_queue(struct dwc3 *dwc, u8 ep_num, struct dwc3_request *req)
+{
+ if (ep_num < 2)
+ return;
+
+ ipc_log_string(dwc->dwc_dma_ipc_log_ctxt,
+ "%02X-%-3.3s %-25.25s 0x%pK", ep_num >> 1,
+ ep_num & 1 ? "IN":"OUT", "QUEUE", &req->request);
+}
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index ddf7a78..6b4dcd9 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -289,9 +289,11 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
if (req->request.status == -EINPROGRESS)
req->request.status = status;
- if (req->trb)
+ if (req->trb) {
+ dbg_ep_unmap(dep->number, req);
usb_gadget_unmap_request_by_dev(dwc->sysdev,
&req->request, req->direction);
+ }
req->trb = NULL;
trace_dwc3_gadget_giveback(req);
@@ -1401,6 +1403,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
else
dwc3_prepare_one_trb_linear(dep, req);
+ dbg_ep_map(dep->number, req);
if (!dwc3_calc_trbs_left(dep))
return;
}
@@ -1545,6 +1548,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
list_add_tail(&req->list, &dep->pending_list);
+ dbg_ep_queue(dep->number, req);
/*
* NOTICE: Isochronous endpoints should NEVER be prestarted. We must
* wait for a XferNotReady event so we will know what's the current
@@ -1704,7 +1708,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
}
out1:
- dbg_event(dep->number, "DEQUEUE", 0);
+ dbg_ep_dequeue(dep->number, req);
/* giveback the request */
dwc3_gadget_giveback(dep, req, -ECONNRESET);
diff --git a/drivers/usb/phy/phy-msm-qusb.c b/drivers/usb/phy/phy-msm-qusb.c
index 93fc8fd..ce1afb0 100644
--- a/drivers/usb/phy/phy-msm-qusb.c
+++ b/drivers/usb/phy/phy-msm-qusb.c
@@ -99,6 +99,7 @@ struct qusb_phy {
void __iomem *ref_clk_base;
void __iomem *tcsr_clamp_dig_n;
void __iomem *tcsr_conn_box_spare;
+ void __iomem *eud_enable_reg;
struct clk *ref_clk_src;
struct clk *ref_clk;
@@ -404,6 +405,11 @@ static int qusb_phy_init(struct usb_phy *phy)
dev_dbg(phy->dev, "%s\n", __func__);
+ if (qphy->eud_enable_reg && readl_relaxed(qphy->eud_enable_reg)) {
+ dev_err(qphy->phy.dev, "eud is enabled\n");
+ return 0;
+ }
+
/*
* ref clock is enabled by default after power on reset. Linux clock
* driver will disable this clock as part of late init if peripheral
@@ -717,6 +723,11 @@ static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev)
dev_dbg(qphy->phy.dev, "%s dpdm_enable:%d\n",
__func__, qphy->dpdm_enable);
+ if (qphy->eud_enable_reg && readl_relaxed(qphy->eud_enable_reg)) {
+ dev_err(qphy->phy.dev, "eud is enabled\n");
+ return 0;
+ }
+
mutex_lock(&qphy->phy_lock);
if (!qphy->dpdm_enable) {
ret = qusb_phy_enable_power(qphy, true);
@@ -904,6 +915,16 @@ static int qusb_phy_probe(struct platform_device *pdev)
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "eud_enable_reg");
+ if (res) {
+ qphy->eud_enable_reg = devm_ioremap_resource(dev, res);
+ if (IS_ERR(qphy->eud_enable_reg)) {
+ dev_err(dev, "err getting eud_enable_reg address\n");
+ return PTR_ERR(qphy->eud_enable_reg);
+ }
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"ref_clk_addr");
if (res) {
qphy->ref_clk_base = devm_ioremap_nocache(dev,
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 3b7988cb..a1877e7 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1745,6 +1745,8 @@ static int f2fs_ioc_getversion(struct file *filp, unsigned long arg)
static int f2fs_ioc_start_atomic_write(struct file *filp)
{
struct inode *inode = file_inode(filp);
+ struct f2fs_inode_info *fi = F2FS_I(inode);
+ struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
int ret;
if (!inode_owner_or_capable(inode))
@@ -1785,6 +1787,12 @@ static int f2fs_ioc_start_atomic_write(struct file *filp)
goto out;
}
+ spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
+ if (list_empty(&fi->inmem_ilist))
+ list_add_tail(&fi->inmem_ilist, &sbi->inode_list[ATOMIC_FILE]);
+ spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
+
+ /* add inode in inmem_list first and set atomic_file */
set_inode_flag(inode, FI_ATOMIC_FILE);
clear_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
@@ -1826,11 +1834,8 @@ static int f2fs_ioc_commit_atomic_write(struct file *filp)
goto err_out;
ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 0, true);
- if (!ret) {
- clear_inode_flag(inode, FI_ATOMIC_FILE);
- F2FS_I(inode)->i_gc_failures[GC_FAILURE_ATOMIC] = 0;
- stat_dec_atomic_write(inode);
- }
+ if (!ret)
+ f2fs_drop_inmem_pages(inode);
} else {
ret = f2fs_do_sync_file(filp, 0, LLONG_MAX, 1, false);
}
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index ac824f6..d71a143 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -185,8 +185,6 @@ bool f2fs_need_SSR(struct f2fs_sb_info *sbi)
void f2fs_register_inmem_page(struct inode *inode, struct page *page)
{
- struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct f2fs_inode_info *fi = F2FS_I(inode);
struct inmem_pages *new;
f2fs_trace_pid(page);
@@ -200,15 +198,11 @@ void f2fs_register_inmem_page(struct inode *inode, struct page *page)
INIT_LIST_HEAD(&new->list);
/* increase reference count with clean state */
- mutex_lock(&fi->inmem_lock);
get_page(page);
- list_add_tail(&new->list, &fi->inmem_pages);
- spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
- if (list_empty(&fi->inmem_ilist))
- list_add_tail(&fi->inmem_ilist, &sbi->inode_list[ATOMIC_FILE]);
- spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
+ mutex_lock(&F2FS_I(inode)->inmem_lock);
+ list_add_tail(&new->list, &F2FS_I(inode)->inmem_pages);
inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);
- mutex_unlock(&fi->inmem_lock);
+ mutex_unlock(&F2FS_I(inode)->inmem_lock);
trace_f2fs_register_inmem_page(page, INMEM);
}
@@ -330,19 +324,17 @@ void f2fs_drop_inmem_pages(struct inode *inode)
mutex_lock(&fi->inmem_lock);
__revoke_inmem_pages(inode, &fi->inmem_pages,
true, false, true);
-
- if (list_empty(&fi->inmem_pages)) {
- spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
- if (!list_empty(&fi->inmem_ilist))
- list_del_init(&fi->inmem_ilist);
- spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
- }
mutex_unlock(&fi->inmem_lock);
}
clear_inode_flag(inode, FI_ATOMIC_FILE);
fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0;
stat_dec_atomic_write(inode);
+
+ spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
+ if (!list_empty(&fi->inmem_ilist))
+ list_del_init(&fi->inmem_ilist);
+ spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
}
void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
@@ -471,11 +463,6 @@ int f2fs_commit_inmem_pages(struct inode *inode)
mutex_lock(&fi->inmem_lock);
err = __f2fs_commit_inmem_pages(inode);
-
- spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
- if (!list_empty(&fi->inmem_ilist))
- list_del_init(&fi->inmem_ilist);
- spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
mutex_unlock(&fi->inmem_lock);
clear_inode_flag(inode, FI_ATOMIC_COMMIT);
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index d0a8920..57cf4b6 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -540,7 +540,7 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
struct ovl_cattr *attr, bool origin)
{
int err;
- const struct cred *old_cred;
+ const struct cred *old_cred, *hold_cred = NULL;
struct cred *override_cred;
struct dentry *parent = dentry->d_parent;
@@ -575,7 +575,7 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
goto out_revert_creds;
}
}
- put_cred(override_creds(override_cred));
+ hold_cred = override_creds(override_cred);
put_cred(override_cred);
if (!ovl_dentry_is_whiteout(dentry))
@@ -584,7 +584,9 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
err = ovl_create_over_whiteout(dentry, inode, attr);
}
out_revert_creds:
- ovl_revert_creds(old_cred);
+ ovl_revert_creds(old_cred ?: hold_cred);
+ if (old_cred && hold_cred)
+ put_cred(hold_cred);
return err;
}
diff --git a/kernel/module.c b/kernel/module.c
index 8644c18..984fdb8 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2187,6 +2187,10 @@ static void free_module(struct module *mod)
/* Finally, free the core (containing the module structure) */
disable_ro_nx(&mod->core_layout);
+#ifdef CONFIG_DEBUG_MODULE_LOAD_INFO
+ pr_info("Unloaded %s: module core layout, start: 0x%pK size: 0x%x\n",
+ mod->name, mod->core_layout.base, mod->core_layout.size);
+#endif
module_memfree(mod->core_layout.base);
}
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 6c164cd..f3d53fc 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -155,6 +155,18 @@
actual pointer values, ignoring the kptr_restrict setting.
Not to be enabled on production builds.
+config DEBUG_MODULE_LOAD_INFO
+ bool "Use prints for module info under a debug flag"
+ help
+ If you say Y here the resulting kernel image will include
+ debug prints which was kept under DEBUG_MODULE_LOAD_INFO.
+ This will be used by developer to debug loadable modules in
+ the kernel.
+ Say Y here only if you plan to debug the kernel.
+ Not to be enabled on production builds.
+
+ If unsure, say N.
+
endmenu # "printk and dmesg options"
menu "Compile-time checks and compiler options"
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 4272af2..23c82c3 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -1256,12 +1256,10 @@ void add_to_oom_reaper(struct task_struct *p)
task_unlock(p);
- if (strcmp(current->comm, ULMK_MAGIC) && __ratelimit(&reaper_rs)
+ if (!strcmp(current->comm, ULMK_MAGIC) && __ratelimit(&reaper_rs)
&& p->signal->oom_score_adj == 0) {
show_mem(SHOW_MEM_FILTER_NODES, NULL);
show_mem_call_notifiers();
- if (sysctl_oom_dump_tasks)
- dump_tasks(NULL, NULL);
}
put_task_struct(p);
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 2cc2ec7..dc2287c 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1162,7 +1162,7 @@ const char * const vmstat_text[] = {
"nr_vmscan_immediate_reclaim",
"nr_dirtied",
"nr_written",
- "", /* nr_indirectly_reclaimable */
+ "nr_indirectly_reclaimable",
"nr_unreclaimable_pages",
/* enum writeback_stat_item counters */