diff options
| author | Arun KS <arunks@codeaurora.org> | 2017-04-06 15:45:04 +0530 |
|---|---|---|
| committer | Arun KS <arunks@codeaurora.org> | 2017-04-06 15:45:04 +0530 |
| commit | e8a49b120cbc3a0472aebcd0f8b06cf95f775a54 (patch) | |
| tree | 7b58ec70803bbfda534ed2a8b50d321a1ecb22ba /drivers/soc | |
| parent | 9a0d24cf9f0e1d4cad7cf92f7e50939fb605e075 (diff) | |
| parent | a3851309dbf7e919b27e2ec927ba3f6350347dff (diff) | |
Merge remote-tracking branch 'remotes/origin/msm-4.4' into dev/msm-4.4-8996au
Conflicts:
arch/arm/boot/dts/qcom/msm8996pro.dtsi
arch/arm64/kernel/Makefile
drivers/leds/leds-qpnp-flash.c
sound/soc/msm/apq8096-auto.c
Change-Id: Idea5d05fec354b8f38ea70643decb03f7b80ddb7
Signed-off-by: Arun KS <arunks@codeaurora.org>
Diffstat (limited to 'drivers/soc')
25 files changed, 1612 insertions, 833 deletions
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c index 09c50a81a943..cc809cbdd839 100644 --- a/drivers/soc/qcom/glink.c +++ b/drivers/soc/qcom/glink.c @@ -4187,7 +4187,6 @@ static void glink_core_channel_cleanup(struct glink_core_xprt_ctx *xprt_ptr) if (ctx->local_open_state == GLINK_CHANNEL_OPENED || ctx->local_open_state == GLINK_CHANNEL_OPENING) { ctx->transport_ptr = dummy_xprt_ctx; - rwref_write_put(&ctx->ch_state_lhb2); glink_core_move_ch_node(xprt_ptr, dummy_xprt_ctx, ctx); } else { /* local state is in either CLOSED or CLOSING */ @@ -4197,9 +4196,9 @@ static void glink_core_channel_cleanup(struct glink_core_xprt_ctx *xprt_ptr) /* Channel should be fully closed now. Delete here */ if (ch_is_fully_closed(ctx)) glink_delete_ch_from_list(ctx, false); - rwref_write_put(&ctx->ch_state_lhb2); } rwref_put(&ctx->ch_state_lhb2); + rwref_write_put(&ctx->ch_state_lhb2); ctx = get_first_ch_ctx(xprt_ptr); } spin_lock_irqsave(&xprt_ptr->xprt_ctx_lock_lhb1, flags); diff --git a/drivers/soc/qcom/glink_private.h b/drivers/soc/qcom/glink_private.h index cdd6988418f7..24053c853a83 100644 --- a/drivers/soc/qcom/glink_private.h +++ b/drivers/soc/qcom/glink_private.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -693,6 +693,7 @@ enum ssr_command { * edge: The G-Link edge name for the channel associated with * this callback data * do_cleanup_data: Structure containing the G-Link SSR do_cleanup message. + * cb_kref: Kref object to maintain cb_data reference. */ struct ssr_notify_data { bool tx_done; @@ -700,6 +701,7 @@ struct ssr_notify_data { bool responded; const char *edge; struct do_cleanup_msg *do_cleanup_data; + struct kref cb_kref; }; /** @@ -734,6 +736,7 @@ struct subsys_info { int notify_list_len; bool link_up; spinlock_t link_up_lock; + spinlock_t cb_lock; }; /** diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c index 14cf10b92122..2dc4208cbc51 100644 --- a/drivers/soc/qcom/glink_smem_native_xprt.c +++ b/drivers/soc/qcom/glink_smem_native_xprt.c @@ -213,6 +213,7 @@ struct edge_info { bool tx_blocked_signal_sent; struct kthread_work kwork; struct kthread_worker kworker; + struct work_struct wakeup_work; struct task_struct *task; struct tasklet_struct tasklet; struct srcu_struct use_ref; @@ -826,7 +827,6 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) int i; bool granted; unsigned long flags; - bool trigger_wakeup = false; int rcu_id; uint16_t rcid; uint32_t name_len; @@ -851,22 +851,10 @@ static void __rx_worker(struct edge_info *einfo, bool atomic_ctx) srcu_read_unlock(&einfo->use_ref, rcu_id); return; } - if (!atomic_ctx) { - if (einfo->tx_resume_needed && fifo_write_avail(einfo)) { - einfo->tx_resume_needed = false; - einfo->xprt_if.glink_core_if_ptr->tx_resume( - &einfo->xprt_if); - } - spin_lock_irqsave(&einfo->write_lock, flags); - if (waitqueue_active(&einfo->tx_blocked_queue)) { - einfo->tx_blocked_signal_sent = false; - trigger_wakeup = true; - } - spin_unlock_irqrestore(&einfo->write_lock, flags); - if (trigger_wakeup) - wake_up_all(&einfo->tx_blocked_queue); - } + if ((atomic_ctx) && ((einfo->tx_resume_needed) || + (waitqueue_active(&einfo->tx_blocked_queue)))) /* tx waiting ?*/ + schedule_work(&einfo->wakeup_work); /* * Access to the fifo needs to be synchronized, however only the calls @@ -1174,6 +1162,39 @@ static void rx_worker_atomic(unsigned long param) } /** + * tx_wakeup_worker() - worker function to wakeup tx blocked thread + * @work: kwork associated with the edge to process commands on. + */ +static void tx_wakeup_worker(struct work_struct *work) +{ + struct edge_info *einfo; + bool trigger_wakeup = false; + unsigned long flags; + int rcu_id; + + einfo = container_of(work, struct edge_info, wakeup_work); + rcu_id = srcu_read_lock(&einfo->use_ref); + if (einfo->in_ssr) { + srcu_read_unlock(&einfo->use_ref, rcu_id); + return; + } + if (einfo->tx_resume_needed && fifo_write_avail(einfo)) { + einfo->tx_resume_needed = false; + einfo->xprt_if.glink_core_if_ptr->tx_resume( + &einfo->xprt_if); + } + spin_lock_irqsave(&einfo->write_lock, flags); + if (waitqueue_active(&einfo->tx_blocked_queue)) { /* tx waiting ?*/ + einfo->tx_blocked_signal_sent = false; + trigger_wakeup = true; + } + spin_unlock_irqrestore(&einfo->write_lock, flags); + if (trigger_wakeup) + wake_up_all(&einfo->tx_blocked_queue); + srcu_read_unlock(&einfo->use_ref, rcu_id); +} + +/** * rx_worker() - worker function to process received commands * @work: kwork associated with the edge to process commands on. */ @@ -2275,6 +2296,7 @@ static int glink_smem_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; @@ -2374,6 +2396,7 @@ request_irq_fail: reg_xprt_fail: smem_alloc_fail: flush_kthread_worker(&einfo->kworker); + flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); @@ -2462,6 +2485,7 @@ static int glink_rpm_native_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->intentless = true; einfo->read_from_fifo = memcpy32_fromio; @@ -2622,6 +2646,7 @@ request_irq_fail: reg_xprt_fail: toc_init_fail: flush_kthread_worker(&einfo->kworker); + flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); @@ -2753,6 +2778,7 @@ static int glink_mailbox_probe(struct platform_device *pdev) init_waitqueue_head(&einfo->tx_blocked_queue); init_kthread_work(&einfo->kwork, rx_worker); init_kthread_worker(&einfo->kworker); + INIT_WORK(&einfo->wakeup_work, tx_wakeup_worker); tasklet_init(&einfo->tasklet, rx_worker_atomic, (unsigned long)einfo); einfo->read_from_fifo = read_from_fifo; einfo->write_to_fifo = write_to_fifo; @@ -2873,6 +2899,7 @@ request_irq_fail: reg_xprt_fail: smem_alloc_fail: flush_kthread_worker(&einfo->kworker); + flush_work(&einfo->wakeup_work); kthread_stop(einfo->task); einfo->task = NULL; tasklet_kill(&einfo->tasklet); diff --git a/drivers/soc/qcom/glink_ssr.c b/drivers/soc/qcom/glink_ssr.c index 5e2dbc8b1d20..7e23b0bc3852 100644 --- a/drivers/soc/qcom/glink_ssr.c +++ b/drivers/soc/qcom/glink_ssr.c @@ -115,6 +115,44 @@ static LIST_HEAD(subsystem_list); static atomic_t responses_remaining = ATOMIC_INIT(0); static wait_queue_head_t waitqueue; +/** + * cb_data_release() - Free cb_data and set to NULL + * @kref_ptr: pointer to kref. + * + * This function releses cb_data. + */ +static inline void cb_data_release(struct kref *kref_ptr) +{ + struct ssr_notify_data *cb_data; + + cb_data = container_of(kref_ptr, struct ssr_notify_data, cb_kref); + kfree(cb_data); +} + +/** + * check_and_get_cb_data() - Try to get reference to kref of cb_data + * @ss_info: pointer to subsystem info structure. + * + * Return: NULL is cb_data is NULL, pointer to cb_data otherwise + */ +static struct ssr_notify_data *check_and_get_cb_data( + struct subsys_info *ss_info) +{ + struct ssr_notify_data *cb_data; + unsigned long flags; + + spin_lock_irqsave(&ss_info->cb_lock, flags); + if (ss_info->cb_data == NULL) { + GLINK_SSR_LOG("<SSR> %s: cb_data is NULL\n", __func__); + spin_unlock_irqrestore(&ss_info->cb_lock, flags); + return 0; + } + kref_get(&ss_info->cb_data->cb_kref); + cb_data = ss_info->cb_data; + spin_unlock_irqrestore(&ss_info->cb_lock, flags); + return cb_data; +} + static void rx_done_cb_worker(struct work_struct *work) { struct rx_done_ch_work *rx_done_work = @@ -338,8 +376,10 @@ void close_ch_worker(struct work_struct *work) ss_info->link_state_handle = link_state_handle; BUG_ON(!ss_info->cb_data); - kfree(ss_info->cb_data); + spin_lock_irqsave(&ss_info->cb_lock, flags); + kref_put(&ss_info->cb_data->cb_kref, cb_data_release); ss_info->cb_data = NULL; + spin_unlock_irqrestore(&ss_info->cb_lock, flags); kfree(close_work); } @@ -507,13 +547,18 @@ int notify_for_subsystem(struct subsys_info *ss_info) return -ENODEV; } handle = ss_info_channel->handle; - ss_leaf_entry->cb_data = ss_info_channel->cb_data; + ss_leaf_entry->cb_data = check_and_get_cb_data( + ss_info_channel); + if (!ss_leaf_entry->cb_data) { + GLINK_SSR_LOG("<SSR> %s: CB data is NULL\n", __func__); + atomic_dec(&responses_remaining); + continue; + } spin_lock_irqsave(&ss_info->link_up_lock, flags); if (IS_ERR_OR_NULL(ss_info_channel->handle) || - !ss_info_channel->cb_data || !ss_info_channel->link_up || - ss_info_channel->cb_data->event + ss_leaf_entry->cb_data->event != GLINK_CONNECTED) { GLINK_SSR_LOG( @@ -526,6 +571,8 @@ int notify_for_subsystem(struct subsys_info *ss_info) spin_unlock_irqrestore(&ss_info->link_up_lock, flags); atomic_dec(&responses_remaining); + kref_put(&ss_leaf_entry->cb_data->cb_kref, + cb_data_release); continue; } spin_unlock_irqrestore(&ss_info->link_up_lock, flags); @@ -536,6 +583,8 @@ int notify_for_subsystem(struct subsys_info *ss_info) GLINK_SSR_ERR( "%s %s: Could not allocate do_cleanup_msg\n", "<SSR>", __func__); + kref_put(&ss_leaf_entry->cb_data->cb_kref, + cb_data_release); return -ENOMEM; } @@ -567,6 +616,8 @@ int notify_for_subsystem(struct subsys_info *ss_info) __func__); } atomic_dec(&responses_remaining); + kref_put(&ss_leaf_entry->cb_data->cb_kref, + cb_data_release); continue; } @@ -596,10 +647,12 @@ int notify_for_subsystem(struct subsys_info *ss_info) __func__); } atomic_dec(&responses_remaining); + kref_put(&ss_leaf_entry->cb_data->cb_kref, + cb_data_release); continue; } - sequence_number++; + kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release); } wait_ret = wait_event_timeout(waitqueue, @@ -608,6 +661,21 @@ int notify_for_subsystem(struct subsys_info *ss_info) list_for_each_entry(ss_leaf_entry, &ss_info->notify_list, notify_list_node) { + ss_info_channel = + get_info_for_subsystem(ss_leaf_entry->ssr_name); + if (ss_info_channel == NULL) { + GLINK_SSR_ERR( + "<SSR> %s: unable to find subsystem name\n", + __func__); + continue; + } + + ss_leaf_entry->cb_data = check_and_get_cb_data( + ss_info_channel); + if (!ss_leaf_entry->cb_data) { + GLINK_SSR_LOG("<SSR> %s: CB data is NULL\n", __func__); + continue; + } if (!wait_ret && !IS_ERR_OR_NULL(ss_leaf_entry->cb_data) && !ss_leaf_entry->cb_data->responded) { GLINK_SSR_ERR("%s %s: Subsystem %s %s\n", @@ -626,6 +694,7 @@ int notify_for_subsystem(struct subsys_info *ss_info) if (!IS_ERR_OR_NULL(ss_leaf_entry->cb_data)) ss_leaf_entry->cb_data->responded = false; + kref_put(&ss_leaf_entry->cb_data->cb_kref, cb_data_release); } complete(¬ifications_successful_complete); return 0; @@ -644,6 +713,7 @@ static int configure_and_open_channel(struct subsys_info *ss_info) struct glink_open_config open_cfg; struct ssr_notify_data *cb_data = NULL; void *handle = NULL; + unsigned long flags; if (!ss_info) { GLINK_SSR_ERR("<SSR> %s: ss_info structure invalid\n", @@ -660,7 +730,10 @@ static int configure_and_open_channel(struct subsys_info *ss_info) cb_data->responded = false; cb_data->event = GLINK_SSR_EVENT_INIT; cb_data->edge = ss_info->edge; + spin_lock_irqsave(&ss_info->cb_lock, flags); ss_info->cb_data = cb_data; + kref_init(&cb_data->cb_kref); + spin_unlock_irqrestore(&ss_info->cb_lock, flags); memset(&open_cfg, 0, sizeof(struct glink_open_config)); @@ -876,6 +949,7 @@ static int glink_ssr_probe(struct platform_device *pdev) ss_info->link_state_handle = NULL; ss_info->cb_data = NULL; spin_lock_init(&ss_info->link_up_lock); + spin_lock_init(&ss_info->cb_lock); nb = kmalloc(sizeof(struct restart_notifier_block), GFP_KERNEL); if (!nb) { diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 768871ffa9a7..0b35caa86d51 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -48,6 +48,11 @@ #include <soc/qcom/socinfo.h> #include <soc/qcom/ramdump.h> +#ifdef CONFIG_WCNSS_MEM_PRE_ALLOC +#include <net/cnss_prealloc.h> +#endif + + #include "wlan_firmware_service_v01.h" #ifdef CONFIG_ICNSS_DEBUG @@ -62,7 +67,7 @@ module_param(qmi_timeout, ulong, 0600); #define WLFW_CLIENT_ID 0x4b4e454c #define MAX_PROP_SIZE 32 #define NUM_LOG_PAGES 10 -#define NUM_REG_LOG_PAGES 4 +#define NUM_LOG_LONG_PAGES 4 #define ICNSS_MAGIC 0x5abc5abc #define ICNSS_SERVICE_LOCATION_CLIENT_NAME "ICNSS-WLAN" @@ -77,6 +82,11 @@ module_param(qmi_timeout, ulong, 0600); ipc_log_string(icnss_ipc_log_context, _x); \ } while (0) +#define icnss_ipc_log_long_string(_x...) do { \ + if (icnss_ipc_log_long_context) \ + ipc_log_string(icnss_ipc_log_long_context, _x); \ + } while (0) + #define icnss_pr_err(_fmt, ...) do { \ pr_err(_fmt, ##__VA_ARGS__); \ icnss_ipc_log_string("ERR: " pr_fmt(_fmt), \ @@ -101,22 +111,25 @@ module_param(qmi_timeout, ulong, 0600); ##__VA_ARGS__); \ } while (0) +#define icnss_pr_vdbg(_fmt, ...) do { \ + pr_debug(_fmt, ##__VA_ARGS__); \ + icnss_ipc_log_long_string("DBG: " pr_fmt(_fmt), \ + ##__VA_ARGS__); \ + } while (0) + #ifdef CONFIG_ICNSS_DEBUG #define ICNSS_ASSERT(_condition) do { \ if (!(_condition)) { \ - icnss_pr_err("ASSERT at line %d\n", \ - __LINE__); \ + icnss_pr_err("ASSERT at line %d\n", __LINE__); \ BUG_ON(1); \ } \ } while (0) + +bool ignore_qmi_timeout; +#define ICNSS_QMI_ASSERT() ICNSS_ASSERT(ignore_qmi_timeout) #else -#define ICNSS_ASSERT(_condition) do { \ - if (!(_condition)) { \ - icnss_pr_err("ASSERT at line %d\n", \ - __LINE__); \ - WARN_ON(1); \ - } \ - } while (0) +#define ICNSS_ASSERT(_condition) do { } while (0) +#define ICNSS_QMI_ASSERT() do { } while (0) #endif enum icnss_debug_quirks { @@ -141,6 +154,7 @@ uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01; module_param(dynamic_feature_mask, ullong, 0600); void *icnss_ipc_log_context; +void *icnss_ipc_log_long_context; #define ICNSS_EVENT_PENDING 2989 @@ -195,6 +209,38 @@ struct ce_irq_list { irqreturn_t (*handler)(int, void *); }; +struct icnss_vreg_info { + struct regulator *reg; + const char *name; + u32 min_v; + u32 max_v; + u32 load_ua; + unsigned long settle_delay; + bool required; +}; + +struct icnss_clk_info { + struct clk *handle; + const char *name; + u32 freq; + bool required; +}; + +static struct icnss_vreg_info icnss_vreg_info[] = { + {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false}, + {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false}, + {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false}, + {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false}, +}; + +#define ICNSS_VREG_INFO_SIZE ARRAY_SIZE(icnss_vreg_info) + +static struct icnss_clk_info icnss_clk_info[] = { + {NULL, "cxo_ref_clk_pin", 0, false}, +}; + +#define ICNSS_CLK_INFO_SIZE ARRAY_SIZE(icnss_clk_info) + struct icnss_stats { struct { uint32_t posted; @@ -248,6 +294,7 @@ struct icnss_stats { uint32_t rejuvenate_ack_req; uint32_t rejuvenate_ack_resp; uint32_t rejuvenate_ack_err; + uint32_t trigger_recovery; }; #define MAX_NO_OF_MAC_ADDR 4 @@ -267,6 +314,8 @@ static struct icnss_priv { struct platform_device *pdev; struct icnss_driver_ops *ops; struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS]; + struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE]; + struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE]; u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS]; phys_addr_t mem_base_pa; void __iomem *mem_base_va; @@ -293,8 +342,9 @@ static struct icnss_priv { u32 pwr_pin_result; u32 phy_io_pin_result; u32 rf_pin_result; + uint32_t nr_mem_region; struct icnss_mem_region_info - icnss_mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01]; + mem_region[QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01]; struct dentry *root_dentry; spinlock_t on_off_lock; struct icnss_stats stats; @@ -320,12 +370,21 @@ static struct icnss_priv { bool bypass_s1_smmu; } *penv; +#ifdef CONFIG_ICNSS_DEBUG +static void icnss_ignore_qmi_timeout(bool ignore) +{ + ignore_qmi_timeout = ignore; +} +#else +static void icnss_ignore_qmi_timeout(bool ignore) { } +#endif + static void icnss_pm_stay_awake(struct icnss_priv *priv) { if (atomic_inc_return(&priv->pm_count) != 1) return; - icnss_pr_dbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state, + icnss_pr_vdbg("PM stay awake, state: 0x%lx, count: %d\n", priv->state, atomic_read(&priv->pm_count)); pm_stay_awake(&priv->pdev->dev); @@ -342,7 +401,7 @@ static void icnss_pm_relax(struct icnss_priv *priv) if (r != 0) return; - icnss_pr_dbg("PM relax, state: 0x%lx, count: %d\n", priv->state, + icnss_pr_vdbg("PM relax, state: 0x%lx, count: %d\n", priv->state, atomic_read(&priv->pm_count)); pm_relax(&priv->pdev->dev); @@ -664,41 +723,220 @@ out: return ret; } +static int icnss_vreg_on(struct icnss_priv *priv) +{ + int ret = 0; + struct icnss_vreg_info *vreg_info; + int i; + + for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) { + vreg_info = &priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + icnss_pr_vdbg("Regulator %s being enabled\n", vreg_info->name); + + ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v, + vreg_info->max_v); + if (ret) { + icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n", + vreg_info->name, vreg_info->min_v, + vreg_info->max_v, ret); + break; + } + + if (vreg_info->load_ua) { + ret = regulator_set_load(vreg_info->reg, + vreg_info->load_ua); + if (ret < 0) { + icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n", + vreg_info->name, + vreg_info->load_ua, ret); + break; + } + } + + ret = regulator_enable(vreg_info->reg); + if (ret) { + icnss_pr_err("Regulator %s, can't enable: %d\n", + vreg_info->name, ret); + break; + } + + if (vreg_info->settle_delay) + udelay(vreg_info->settle_delay); + } + + if (!ret) + return 0; + + for (; i >= 0; i--) { + vreg_info = &priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + regulator_disable(vreg_info->reg); + regulator_set_load(vreg_info->reg, 0); + regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v); + } + + return ret; +} + +static int icnss_vreg_off(struct icnss_priv *priv) +{ + int ret = 0; + struct icnss_vreg_info *vreg_info; + int i; + + for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) { + vreg_info = &priv->vreg_info[i]; + + if (!vreg_info->reg) + continue; + + icnss_pr_vdbg("Regulator %s being disabled\n", vreg_info->name); + + ret = regulator_disable(vreg_info->reg); + if (ret) + icnss_pr_err("Regulator %s, can't disable: %d\n", + vreg_info->name, ret); + + ret = regulator_set_load(vreg_info->reg, 0); + if (ret < 0) + icnss_pr_err("Regulator %s, can't set load: %d\n", + vreg_info->name, ret); + + ret = regulator_set_voltage(vreg_info->reg, 0, + vreg_info->max_v); + if (ret) + icnss_pr_err("Regulator %s, can't set voltage: %d\n", + vreg_info->name, ret); + } + + return ret; +} + +static int icnss_clk_init(struct icnss_priv *priv) +{ + struct icnss_clk_info *clk_info; + int i; + int ret = 0; + + for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) { + clk_info = &priv->clk_info[i]; + + if (!clk_info->handle) + continue; + + icnss_pr_vdbg("Clock %s being enabled\n", clk_info->name); + + if (clk_info->freq) { + ret = clk_set_rate(clk_info->handle, clk_info->freq); + + if (ret) { + icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n", + clk_info->name, clk_info->freq, + ret); + break; + } + } + + ret = clk_prepare_enable(clk_info->handle); + if (ret) { + icnss_pr_err("Clock %s, can't enable: %d\n", + clk_info->name, ret); + break; + } + } + + if (ret == 0) + return 0; + + for (; i >= 0; i--) { + clk_info = &priv->clk_info[i]; + + if (!clk_info->handle) + continue; + + clk_disable_unprepare(clk_info->handle); + } + + return ret; +} + +static int icnss_clk_deinit(struct icnss_priv *priv) +{ + struct icnss_clk_info *clk_info; + int i; + + for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) { + clk_info = &priv->clk_info[i]; + + if (!clk_info->handle) + continue; + + icnss_pr_vdbg("Clock %s being disabled\n", clk_info->name); + + clk_disable_unprepare(clk_info->handle); + } + + return 0; +} + static int icnss_hw_power_on(struct icnss_priv *priv) { int ret = 0; - unsigned long flags; icnss_pr_dbg("HW Power on: state: 0x%lx\n", priv->state); - spin_lock_irqsave(&priv->on_off_lock, flags); + spin_lock(&priv->on_off_lock); if (test_bit(ICNSS_POWER_ON, &priv->state)) { - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); return ret; } set_bit(ICNSS_POWER_ON, &priv->state); - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); + + ret = icnss_vreg_on(priv); + if (ret) + goto out; + + ret = icnss_clk_init(priv); + if (ret) + goto vreg_off; return ret; + +vreg_off: + icnss_vreg_off(priv); +out: + clear_bit(ICNSS_POWER_ON, &priv->state); + return ret; } static int icnss_hw_power_off(struct icnss_priv *priv) { int ret = 0; - unsigned long flags; if (test_bit(HW_ALWAYS_ON, &quirks)) return 0; icnss_pr_dbg("HW Power off: 0x%lx\n", priv->state); - spin_lock_irqsave(&priv->on_off_lock, flags); + spin_lock(&priv->on_off_lock); if (!test_bit(ICNSS_POWER_ON, &priv->state)) { - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); return ret; } clear_bit(ICNSS_POWER_ON, &priv->state); - spin_unlock_irqrestore(&priv->on_off_lock, flags); + spin_unlock(&priv->on_off_lock); + + icnss_clk_deinit(priv); + + ret = icnss_vreg_off(priv); return ret; } @@ -744,7 +982,7 @@ int icnss_power_off(struct device *dev) } EXPORT_SYMBOL(icnss_power_off); -static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) +static int icnss_map_msa_permissions(struct icnss_mem_region_info *mem_region) { int ret = 0; phys_addr_t addr; @@ -757,10 +995,10 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) int source_nelems = sizeof(source_vmlist)/sizeof(u32); int dest_nelems = 0; - addr = priv->icnss_mem_region[index].reg_addr; - size = priv->icnss_mem_region[index].size; + addr = mem_region->reg_addr; + size = mem_region->size; - if (!priv->icnss_mem_region[index].secure_flag) { + if (!mem_region->secure_flag) { dest_vmids[2] = VMID_WLAN_CE; dest_nelems = 3; } else { @@ -770,19 +1008,20 @@ static int icnss_map_msa_permissions(struct icnss_priv *priv, u32 index) ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems, dest_vmids, dest_perms, dest_nelems); if (ret) { - icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n", - index, &addr, size, ret); + icnss_pr_err("Hyperviser map failed for PA=%pa size=%u err=%d\n", + &addr, size, ret); goto out; } - icnss_pr_dbg("Hypervisor map for region %u: source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n", - index, source_vmlist[0], dest_nelems, - dest_vmids[0], dest_vmids[1], dest_vmids[2]); + + icnss_pr_dbg("Hypervisor map for source=%x, dest_nelems=%d, dest[0]=%x, dest[1]=%x, dest[2]=%x\n", + source_vmlist[0], dest_nelems, dest_vmids[0], + dest_vmids[1], dest_vmids[2]); out: return ret; } -static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) +static int icnss_unmap_msa_permissions(struct icnss_mem_region_info *mem_region) { int ret = 0; phys_addr_t addr; @@ -793,9 +1032,10 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) int source_nelems = 0; int dest_nelems = sizeof(dest_vmids)/sizeof(u32); - addr = priv->icnss_mem_region[index].reg_addr; - size = priv->icnss_mem_region[index].size; - if (!priv->icnss_mem_region[index].secure_flag) { + addr = mem_region->reg_addr; + size = mem_region->size; + + if (!mem_region->secure_flag) { source_vmlist[2] = VMID_WLAN_CE; source_nelems = 3; } else { @@ -806,14 +1046,13 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index) ret = hyp_assign_phys(addr, size, source_vmlist, source_nelems, dest_vmids, dest_perms, dest_nelems); if (ret) { - icnss_pr_err("Region %u hyp_assign_phys failed IPA=%pa size=%u err=%d\n", - index, &addr, size, ret); + icnss_pr_err("Hyperviser unmap failed for PA=%pa size=%u err=%d\n", + &addr, size, ret); goto out; } - icnss_pr_dbg("hypervisor unmap for region %u, source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n", - index, source_nelems, - source_vmlist[0], source_vmlist[1], source_vmlist[2], - dest_vmids[0]); + icnss_pr_dbg("Hypervisor unmap for source_nelems=%d, source[0]=%x, source[1]=%x, source[2]=%x, dest=%x\n", + source_nelems, source_vmlist[0], source_vmlist[1], + source_vmlist[2], dest_vmids[0]); out: return ret; } @@ -821,34 +1060,37 @@ out: static int icnss_setup_msa_permissions(struct icnss_priv *priv) { int ret; + int i; if (test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) return 0; - ret = icnss_map_msa_permissions(priv, 0); - if (ret) - return ret; + for (i = 0; i < priv->nr_mem_region; i++) { - ret = icnss_map_msa_permissions(priv, 1); - if (ret) - goto err_map_msa; + ret = icnss_map_msa_permissions(&priv->mem_region[i]); + if (ret) + goto err_unmap; + } set_bit(ICNSS_MSA0_ASSIGNED, &priv->state); - return ret; + return 0; -err_map_msa: - icnss_unmap_msa_permissions(priv, 0); +err_unmap: + for (i--; i >= 0; i--) + icnss_unmap_msa_permissions(&priv->mem_region[i]); return ret; } static void icnss_remove_msa_permissions(struct icnss_priv *priv) { + int i; + if (!test_bit(ICNSS_MSA0_ASSIGNED, &priv->state)) return; - icnss_unmap_msa_permissions(priv, 0); - icnss_unmap_msa_permissions(priv, 1); + for (i = 0; i < priv->nr_mem_region; i++) + icnss_unmap_msa_permissions(&priv->mem_region[i]); clear_bit(ICNSS_MSA0_ASSIGNED, &priv->state); } @@ -899,7 +1141,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void) icnss_pr_dbg("Receive mem_region_info_len: %d\n", resp.mem_region_info_len); - if (resp.mem_region_info_len > 2) { + if (resp.mem_region_info_len > QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01) { icnss_pr_err("Invalid memory region length received: %d\n", resp.mem_region_info_len); ret = -EINVAL; @@ -907,24 +1149,25 @@ static int wlfw_msa_mem_info_send_sync_msg(void) } penv->stats.msa_info_resp++; + penv->nr_mem_region = resp.mem_region_info_len; for (i = 0; i < resp.mem_region_info_len; i++) { - penv->icnss_mem_region[i].reg_addr = + penv->mem_region[i].reg_addr = resp.mem_region_info[i].region_addr; - penv->icnss_mem_region[i].size = + penv->mem_region[i].size = resp.mem_region_info[i].size; - penv->icnss_mem_region[i].secure_flag = + penv->mem_region[i].secure_flag = resp.mem_region_info[i].secure_flag; icnss_pr_dbg("Memory Region: %d Addr: 0x%llx Size: 0x%x Flag: 0x%08x\n", - i, penv->icnss_mem_region[i].reg_addr, - penv->icnss_mem_region[i].size, - penv->icnss_mem_region[i].secure_flag); + i, penv->mem_region[i].reg_addr, + penv->mem_region[i].size, + penv->mem_region[i].secure_flag); } return 0; out: penv->stats.msa_info_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -972,7 +1215,7 @@ static int wlfw_msa_ready_send_sync_msg(void) out: penv->stats.msa_ready_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1035,7 +1278,7 @@ static int wlfw_ind_register_send_sync_msg(void) out: penv->stats.ind_register_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1104,7 +1347,7 @@ static int wlfw_cap_send_sync_msg(void) out: penv->stats.cap_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1165,7 +1408,7 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode) out: penv->stats.mode_req_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1215,7 +1458,7 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data) out: penv->stats.cfg_req_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1268,7 +1511,7 @@ static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode) out: penv->stats.ini_req_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1434,7 +1677,7 @@ static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv) out: priv->stats.rejuvenate_ack_err++; - ICNSS_ASSERT(false); + ICNSS_QMI_ASSERT(); return ret; } @@ -1508,7 +1751,7 @@ static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work) if (!penv || !penv->wlfw_clnt) return; - icnss_pr_dbg("Receiving Event in work queue context\n"); + icnss_pr_vdbg("Receiving Event in work queue context\n"); do { } while ((ret = qmi_recv_msg(penv->wlfw_clnt)) == 0); @@ -1516,13 +1759,13 @@ static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work) if (ret != -ENOMSG) icnss_pr_err("Error receiving message: %d\n", ret); - icnss_pr_dbg("Receiving Event completed\n"); + icnss_pr_vdbg("Receiving Event completed\n"); } static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle, enum qmi_event_type event, void *notify_priv) { - icnss_pr_dbg("QMI client notify: %d\n", event); + icnss_pr_vdbg("QMI client notify: %d\n", event); if (!penv || !penv->wlfw_clnt) return; @@ -1537,11 +1780,29 @@ static void icnss_qmi_wlfw_clnt_notify(struct qmi_handle *handle, } } +static int icnss_call_driver_uevent(struct icnss_priv *priv, + enum icnss_uevent uevent, void *data) +{ + struct icnss_uevent_data uevent_data; + + if (!priv->ops || !priv->ops->uevent) + return 0; + + icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n", + priv->state, uevent); + + uevent_data.uevent = uevent; + uevent_data.data = data; + + return priv->ops->uevent(&priv->pdev->dev, &uevent_data); +} + static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle, unsigned int msg_id, void *msg, unsigned int msg_len, void *ind_cb_priv) { struct icnss_event_pd_service_down_data *event_data; + struct icnss_uevent_fw_down_data fw_down_data; if (!penv) return; @@ -1566,11 +1827,16 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle, case QMI_WLFW_REJUVENATE_IND_V01: icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n", msg_id, penv->state); + + icnss_ignore_qmi_timeout(true); event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); if (event_data == NULL) return; event_data->crashed = true; event_data->fw_rejuvenate = true; + fw_down_data.crashed = true; + icnss_call_driver_uevent(penv, ICNSS_UEVENT_FW_DOWN, + &fw_down_data); icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, 0, event_data); break; @@ -1684,23 +1950,6 @@ static int icnss_driver_event_server_exit(void *data) return 0; } -static int icnss_call_driver_uevent(struct icnss_priv *priv, - enum icnss_uevent uevent, void *data) -{ - struct icnss_uevent_data uevent_data; - - if (!priv->ops || !priv->ops->uevent) - return 0; - - icnss_pr_dbg("Calling driver uevent state: 0x%lx, uevent: %d\n", - priv->state, uevent); - - uevent_data.uevent = uevent; - uevent_data.data = data; - - return priv->ops->uevent(&priv->pdev->dev, &uevent_data); -} - static int icnss_call_driver_probe(struct icnss_priv *priv) { int ret; @@ -1708,6 +1957,9 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) if (!priv->ops || !priv->ops->probe) return 0; + if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) + return -EINVAL; + icnss_pr_dbg("Calling driver probe state: 0x%lx\n", priv->state); icnss_hw_power_on(priv); @@ -1716,6 +1968,8 @@ static int icnss_call_driver_probe(struct icnss_priv *priv) if (ret < 0) { icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n", ret, priv->state); + wcnss_prealloc_check_memory_leak(); + wcnss_pre_alloc_reset(); goto out; } @@ -1845,6 +2099,8 @@ static int icnss_driver_event_register_driver(void *data) if (ret) { icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n", ret, penv->state); + wcnss_prealloc_check_memory_leak(); + wcnss_pre_alloc_reset(); goto power_off; } @@ -1870,6 +2126,8 @@ static int icnss_driver_event_unregister_driver(void *data) penv->ops->remove(&penv->pdev->dev); clear_bit(ICNSS_DRIVER_PROBED, &penv->state); + wcnss_prealloc_check_memory_leak(); + wcnss_pre_alloc_reset(); penv->ops = NULL; @@ -1894,6 +2152,10 @@ static int icnss_call_driver_remove(struct icnss_priv *priv) penv->ops->remove(&priv->pdev->dev); clear_bit(ICNSS_DRIVER_PROBED, &priv->state); + wcnss_prealloc_check_memory_leak(); + wcnss_pre_alloc_reset(); + + icnss_hw_power_off(penv); return 0; } @@ -1909,7 +2171,8 @@ static int icnss_fw_crashed(struct icnss_priv *priv, icnss_pm_stay_awake(priv); - icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL); + if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) + icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_CRASHED, NULL); if (event_data->wdog_bite) { set_bit(ICNSS_WDOG_BITE, &priv->state); @@ -1932,7 +2195,7 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, struct icnss_event_pd_service_down_data *event_data = data; if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state)) - return 0; + goto out; if (test_bit(ICNSS_PD_RESTART, &priv->state)) { icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n", @@ -1947,10 +2210,10 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv, icnss_call_driver_remove(priv); out: - ret = icnss_hw_power_off(priv); - kfree(data); + icnss_ignore_qmi_timeout(false); + return ret; } @@ -2075,8 +2338,9 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, struct notif_data *notif = data; struct icnss_priv *priv = container_of(nb, struct icnss_priv, modem_ssr_nb); + struct icnss_uevent_fw_down_data fw_down_data; - icnss_pr_dbg("Modem-Notify: event %lu\n", code); + icnss_pr_vdbg("Modem-Notify: event %lu\n", code); if (code == SUBSYS_AFTER_SHUTDOWN && notif->crashed == CRASH_STATUS_ERR_FATAL) { @@ -2092,9 +2356,11 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, if (test_bit(ICNSS_PDR_ENABLED, &priv->state)) return NOTIFY_OK; - icnss_pr_info("Modem went down, state: %lx, crashed: %d\n", + icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n", priv->state, notif->crashed); + icnss_ignore_qmi_timeout(true); + event_data = kzalloc(sizeof(*event_data), GFP_KERNEL); if (event_data == NULL) @@ -2105,6 +2371,9 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, if (notif->crashed == CRASH_STATUS_WDOG_BITE) event_data->wdog_bite = true; + fw_down_data.crashed = !!notif->crashed; + icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data); + icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, ICNSS_EVENT_SYNC, event_data); @@ -2168,6 +2437,7 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, service_notifier_nb); enum pd_subsys_state *state = data; struct icnss_event_pd_service_down_data *event_data; + struct icnss_uevent_fw_down_data fw_down_data; icnss_pr_dbg("PD service notification: 0x%lx state: 0x%lx\n", notification, priv->state); @@ -2201,6 +2471,10 @@ static int icnss_service_notifier_notify(struct notifier_block *nb, } event_post: + icnss_ignore_qmi_timeout(true); + + fw_down_data.crashed = event_data->crashed; + icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data); icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, ICNSS_EVENT_SYNC, event_data); done: @@ -2309,7 +2583,7 @@ static int icnss_pd_restart_enable(struct icnss_priv *priv) return 0; out: - icnss_pr_err("PD restart not enabled: %d\n", ret); + icnss_pr_err("Failed to enable PD restart: %d\n", ret); return ret; } @@ -2419,7 +2693,7 @@ int icnss_ce_request_irq(unsigned int ce_id, goto out; } - icnss_pr_dbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state); + icnss_pr_vdbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state); if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id); @@ -2445,7 +2719,7 @@ int icnss_ce_request_irq(unsigned int ce_id, irq_entry->irq = irq; irq_entry->handler = handler; - icnss_pr_dbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id); + icnss_pr_vdbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id); penv->stats.ce_irqs[ce_id].request++; out: @@ -2464,7 +2738,7 @@ int icnss_ce_free_irq(unsigned int ce_id, void *ctx) goto out; } - icnss_pr_dbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state); + icnss_pr_vdbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state); if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id); @@ -2498,7 +2772,7 @@ void icnss_enable_irq(unsigned int ce_id) return; } - icnss_pr_dbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, + icnss_pr_vdbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, penv->state); if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { @@ -2522,7 +2796,7 @@ void icnss_disable_irq(unsigned int ce_id) return; } - icnss_pr_dbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, + icnss_pr_vdbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, penv->state); if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { @@ -2922,12 +3196,25 @@ int icnss_trigger_recovery(struct device *dev) goto out; } - if (!priv->service_notifier[0].handle) { - icnss_pr_err("Invalid handle during recovery\n"); + if (!test_bit(ICNSS_PDR_ENABLED, &priv->state)) { + icnss_pr_err("PD restart not enabled to trigger recovery: state: 0x%lx\n", + priv->state); + ret = -EOPNOTSUPP; + goto out; + } + + if (!priv->service_notifier || !priv->service_notifier[0].handle) { + icnss_pr_err("Invalid handle during recovery, state: 0x%lx\n", + priv->state); ret = -EINVAL; goto out; } + WARN_ON(1); + icnss_pr_warn("Initiate PD restart at WLAN FW, state: 0x%lx\n", + priv->state); + priv->stats.trigger_recovery++; + /* * Initiate PDR, required only for the first instance */ @@ -3005,6 +3292,114 @@ static void icnss_smmu_deinit(struct icnss_priv *priv) priv->smmu_mapping = NULL; } +static int icnss_get_vreg_info(struct device *dev, + struct icnss_vreg_info *vreg_info) +{ + int ret = 0; + char prop_name[MAX_PROP_SIZE]; + struct regulator *reg; + const __be32 *prop; + int len = 0; + int i; + + reg = devm_regulator_get_optional(dev, vreg_info->name); + if (PTR_ERR(reg) == -EPROBE_DEFER) { + icnss_pr_err("EPROBE_DEFER for regulator: %s\n", + vreg_info->name); + ret = PTR_ERR(reg); + goto out; + } + + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + + if (vreg_info->required) { + icnss_pr_err("Regulator %s doesn't exist: %d\n", + vreg_info->name, ret); + goto out; + } else { + icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n", + vreg_info->name, ret); + goto done; + } + } + + vreg_info->reg = reg; + + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-config", vreg_info->name); + + prop = of_get_property(dev->of_node, prop_name, &len); + + icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n", + prop_name, len); + + if (!prop || len < (2 * sizeof(__be32))) { + icnss_pr_dbg("Property %s %s\n", prop_name, + prop ? "invalid format" : "doesn't exist"); + goto done; + } + + for (i = 0; (i * sizeof(__be32)) < len; i++) { + switch (i) { + case 0: + vreg_info->min_v = be32_to_cpup(&prop[0]); + break; + case 1: + vreg_info->max_v = be32_to_cpup(&prop[1]); + break; + case 2: + vreg_info->load_ua = be32_to_cpup(&prop[2]); + break; + case 3: + vreg_info->settle_delay = be32_to_cpup(&prop[3]); + break; + default: + icnss_pr_dbg("Property %s, ignoring value at %d\n", + prop_name, i); + break; + } + } + +done: + icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n", + vreg_info->name, vreg_info->min_v, vreg_info->max_v, + vreg_info->load_ua, vreg_info->settle_delay); + + return 0; + +out: + return ret; +} + +static int icnss_get_clk_info(struct device *dev, + struct icnss_clk_info *clk_info) +{ + struct clk *handle; + int ret = 0; + + handle = devm_clk_get(dev, clk_info->name); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + if (clk_info->required) { + icnss_pr_err("Clock %s isn't available: %d\n", + clk_info->name, ret); + goto out; + } else { + icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name, + ret); + ret = 0; + goto out; + } + } + + icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq); + + clk_info->handle = handle; +out: + return ret; +} + static int icnss_fw_debug_show(struct seq_file *s, void *data) { struct icnss_priv *priv = s->private; @@ -3015,6 +3410,7 @@ static int icnss_fw_debug_show(struct seq_file *s, void *data) seq_puts(s, " VAL: 0 (Test mode disable)\n"); seq_puts(s, " VAL: 1 (WLAN FW test)\n"); seq_puts(s, " VAL: 2 (CCPM test)\n"); + seq_puts(s, " VAL: 3 (Trigger Recovery)\n"); seq_puts(s, "\nCMD: dynamic_feature_mask\n"); seq_puts(s, " VAL: (64 bit feature mask)\n"); @@ -3370,6 +3766,7 @@ static int icnss_stats_show(struct seq_file *s, void *data) ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp); ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err); + ICNSS_STATS_DUMP(s, priv, trigger_recovery); seq_puts(s, "\n<------------------ PM stats ------------------->\n"); ICNSS_STATS_DUMP(s, priv, pm_suspend); @@ -3715,6 +4112,21 @@ static int icnss_probe(struct platform_device *pdev) if (ret == -EPROBE_DEFER) goto out; + memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info)); + for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) { + ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]); + + if (ret) + goto out; + } + + memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info)); + for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) { + ret = icnss_get_clk_info(dev, &priv->clk_info[i]); + if (ret) + goto out; + } + if (of_property_read_bool(pdev->dev.of_node, "qcom,smmu-s1-bypass")) priv->bypass_s1_smmu = true; @@ -3884,7 +4296,7 @@ static int icnss_pm_suspend(struct device *dev) return -EINVAL; } - icnss_pr_dbg("PM Suspend, state: 0x%lx\n", priv->state); + icnss_pr_vdbg("PM Suspend, state: 0x%lx\n", priv->state); if (!priv->ops || !priv->ops->pm_suspend || !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) @@ -3913,7 +4325,7 @@ static int icnss_pm_resume(struct device *dev) return -EINVAL; } - icnss_pr_dbg("PM resume, state: 0x%lx\n", priv->state); + icnss_pr_vdbg("PM resume, state: 0x%lx\n", priv->state); if (!priv->ops || !priv->ops->pm_resume || !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) @@ -3942,7 +4354,7 @@ static int icnss_pm_suspend_noirq(struct device *dev) return -EINVAL; } - icnss_pr_dbg("PM suspend_noirq, state: 0x%lx\n", priv->state); + icnss_pr_vdbg("PM suspend_noirq, state: 0x%lx\n", priv->state); if (!priv->ops || !priv->ops->suspend_noirq || !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) @@ -3971,7 +4383,7 @@ static int icnss_pm_resume_noirq(struct device *dev) return -EINVAL; } - icnss_pr_dbg("PM resume_noirq, state: 0x%lx\n", priv->state); + icnss_pr_vdbg("PM resume_noirq, state: 0x%lx\n", priv->state); if (!priv->ops || !priv->ops->resume_noirq || !test_bit(ICNSS_DRIVER_PROBED, &priv->state)) @@ -4022,6 +4434,11 @@ static int __init icnss_initialize(void) if (!icnss_ipc_log_context) icnss_pr_err("Unable to create log context\n"); + icnss_ipc_log_long_context = ipc_log_context_create(NUM_LOG_LONG_PAGES, + "icnss_long", 0); + if (!icnss_ipc_log_long_context) + icnss_pr_err("Unable to create log long context\n"); + return platform_driver_register(&icnss_driver); } @@ -4030,6 +4447,8 @@ static void __exit icnss_exit(void) platform_driver_unregister(&icnss_driver); ipc_log_context_destroy(icnss_ipc_log_context); icnss_ipc_log_context = NULL; + ipc_log_context_destroy(icnss_ipc_log_long_context); + icnss_ipc_log_long_context = NULL; } diff --git a/drivers/soc/qcom/ipc_router_mhi_xprt.c b/drivers/soc/qcom/ipc_router_mhi_xprt.c index 9a0624804c21..f9d967fd0af6 100644 --- a/drivers/soc/qcom/ipc_router_mhi_xprt.c +++ b/drivers/soc/qcom/ipc_router_mhi_xprt.c @@ -792,20 +792,14 @@ static int ipc_router_mhi_driver_register( { int rc_status; - rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle, - mhi_xprtp->ch_hndl.out_chan_id, 0, - &mhi_xprtp->ch_hndl.out_clnt_info, - (void *)mhi_xprtp); + rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.out_handle, NULL); if (rc_status) { IPC_RTR_ERR("%s: Error %d registering out_chan for %s\n", __func__, rc_status, mhi_xprtp->xprt_name); return -EFAULT; } - rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle, - mhi_xprtp->ch_hndl.in_chan_id, 0, - &mhi_xprtp->ch_hndl.in_clnt_info, - (void *)mhi_xprtp); + rc_status = mhi_register_channel(&mhi_xprtp->ch_hndl.in_handle, NULL); if (rc_status) { mhi_deregister_channel(mhi_xprtp->ch_hndl.out_handle); IPC_RTR_ERR("%s: Error %d registering in_chan for %s\n", diff --git a/drivers/soc/qcom/mpm-of.c b/drivers/soc/qcom/mpm-of.c index 93f2de8a59dd..77c50528be4b 100644 --- a/drivers/soc/qcom/mpm-of.c +++ b/drivers/soc/qcom/mpm-of.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -129,18 +129,6 @@ static uint32_t *msm_mpm_falling_edge; static uint32_t *msm_mpm_rising_edge; static uint32_t *msm_mpm_polarity; -enum { - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ = BIT(0), - MSM_MPM_DEBUG_PENDING_IRQ = BIT(1), - MSM_MPM_DEBUG_WRITE = BIT(2), - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE = BIT(3), -}; - -static int msm_mpm_debug_mask = 1; -module_param_named( - debug_mask, msm_mpm_debug_mask, int, S_IRUGO | S_IWUSR | S_IWGRP -); - enum mpm_state { MSM_MPM_GIC_IRQ_MAPPING_DONE = BIT(0), MSM_MPM_GPIO_IRQ_MAPPING_DONE = BIT(1), @@ -174,9 +162,6 @@ static inline void msm_mpm_write( unsigned int offset = reg * MSM_MPM_REG_WIDTH + subreg_index + 2; __raw_writel(value, msm_mpm_dev_data.mpm_request_reg_base + offset * 4); - if (MSM_MPM_DEBUG_WRITE & msm_mpm_debug_mask) - pr_info("%s: reg %u.%u: 0x%08x\n", - __func__, reg, subreg_index, value); } static inline void msm_mpm_send_interrupt(void) @@ -513,37 +498,19 @@ int msm_mpm_set_pin_type(unsigned int pin, unsigned int flow_type) static bool msm_mpm_interrupts_detectable(int d, bool from_idle) { unsigned long *irq_bitmap; - bool debug_mask, ret = false; + bool ret = false; struct mpm_irqs *unlisted = &unlisted_irqs[d]; if (!msm_mpm_is_initialized()) return false; - if (from_idle) { + if (from_idle) irq_bitmap = unlisted->enabled_irqs; - debug_mask = msm_mpm_debug_mask & - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ_IDLE; - } else { + else irq_bitmap = unlisted->wakeup_irqs; - debug_mask = msm_mpm_debug_mask & - MSM_MPM_DEBUG_NON_DETECTABLE_IRQ; - } ret = (bool) bitmap_empty(irq_bitmap, unlisted->size); - if (debug_mask && !ret) { - int i = 0; - i = find_first_bit(irq_bitmap, unlisted->size); - pr_info("%s(): %s preventing system sleep modes during %s\n", - __func__, unlisted->domain_name, - from_idle ? "idle" : "suspend"); - - while (i < unlisted->size) { - pr_info("\thwirq: %d\n", i); - i = find_next_bit(irq_bitmap, unlisted->size, i + 1); - } - } - return ret; } @@ -601,10 +568,6 @@ void msm_mpm_exit_sleep(bool from_idle) pending = msm_mpm_read(MSM_MPM_REG_STATUS, i); pending &= enabled_intr[i]; - if (MSM_MPM_DEBUG_PENDING_IRQ & msm_mpm_debug_mask) - pr_info("%s: enabled_intr.%d pending.%d: 0x%08x 0x%08lx\n", - __func__, i, i, enabled_intr[i], pending); - k = find_first_bit(&pending, 32); while (k < 32) { unsigned int mpm_irq = 32 * i + k; diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c index 21e9f17e6a7e..6e5ddc4a3a7d 100644 --- a/drivers/soc/qcom/peripheral-loader.c +++ b/drivers/soc/qcom/peripheral-loader.c @@ -465,6 +465,8 @@ static int pil_alloc_region(struct pil_priv *priv, phys_addr_t min_addr, if (region == NULL) { pil_err(priv->desc, "Failed to allocate relocatable region of size %zx\n", size); + priv->region_start = 0; + priv->region_end = 0; return -ENOMEM; } @@ -920,7 +922,8 @@ out: &desc->attrs); priv->region = NULL; } - pil_clear_segment(desc); + if (desc->clear_fw_region && priv->region_start) + pil_clear_segment(desc); pil_release_mmap(desc); } return ret; diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h index 802abe26a960..9521cf726069 100644 --- a/drivers/soc/qcom/peripheral-loader.h +++ b/drivers/soc/qcom/peripheral-loader.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -37,6 +37,7 @@ struct pil_priv; * This defaults to iounmap if not specified. * @shutdown_fail: Set if PIL op for shutting down subsystem fails. * @modem_ssr: true if modem is restarting, false if booting for first time. + * @clear_fw_region: Clear fw region on failure in loading. * @subsys_vmid: memprot id for the subsystem. */ struct pil_desc { @@ -56,6 +57,7 @@ struct pil_desc { void *map_data; bool shutdown_fail; bool modem_ssr; + bool clear_fw_region; u32 subsys_vmid; }; diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c index 0e023a019280..793edc5b67ed 100644 --- a/drivers/soc/qcom/pil-q6v5-mss.c +++ b/drivers/soc/qcom/pil-q6v5-mss.c @@ -276,8 +276,12 @@ static int pil_mss_loadable_init(struct modem_data *drv, if (q6->cx_ipeak_vote) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cxip_lm_vote_clear"); - q6->cxip_lm_vote_clear = devm_ioremap_resource(&pdev->dev, - res); + if (!res) { + dev_err(&pdev->dev, "Failed to get resource for ipeak reg\n"); + return -EINVAL; + } + q6->cxip_lm_vote_clear = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); if (!q6->cxip_lm_vote_clear) return -ENOMEM; } diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c index a39c2b6aa672..34228f072b28 100644 --- a/drivers/soc/qcom/pil-q6v5.c +++ b/drivers/soc/qcom/pil-q6v5.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -588,6 +588,7 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev) if (ret) return ERR_PTR(ret); + desc->clear_fw_region = false; desc->dev = &pdev->dev; drv->qdsp6v5_2_0 = of_device_is_compatible(pdev->dev.of_node, diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c index d11ffdde23be..3a6d84140bc9 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c +++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c @@ -98,8 +98,7 @@ static int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, unsigned long flags; spin_lock_irqsave(&apr_ch->w_lock, flags); - rc = glink_tx(apr_ch->handle, pkt_priv, data, len, - GLINK_TX_REQ_INTENT | GLINK_TX_ATOMIC); + rc = glink_tx(apr_ch->handle, pkt_priv, data, len, GLINK_TX_ATOMIC); spin_unlock_irqrestore(&apr_ch->w_lock, flags); if (rc) diff --git a/drivers/soc/qcom/qdsp6v2/audio_notifier.c b/drivers/soc/qcom/qdsp6v2/audio_notifier.c index b120883afbb0..a59b436234c7 100644 --- a/drivers/soc/qcom/qdsp6v2/audio_notifier.c +++ b/drivers/soc/qcom/qdsp6v2/audio_notifier.c @@ -626,9 +626,11 @@ static int __init audio_notifier_late_init(void) * If pdr registration failed, register clients on next service * Do in late init to ensure that SSR subsystem is initialized */ + mutex_lock(¬ifier_mutex); if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE)) audio_notifer_reg_all_clients(); + mutex_unlock(¬ifier_mutex); return 0; } late_initcall(audio_notifier_late_init); diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c index 10f71b85a15b..fe5458974406 100644 --- a/drivers/soc/qcom/qdsp6v2/voice_svc.c +++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c @@ -368,6 +368,9 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf, struct voice_svc_prvt *prtd; struct voice_svc_write_msg *data = NULL; uint32_t cmd; + struct voice_svc_register *register_data = NULL; + struct voice_svc_cmd_request *request_data = NULL; + uint32_t request_payload_size; pr_debug("%s\n", __func__); @@ -416,12 +419,19 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf, */ if (count == (sizeof(struct voice_svc_write_msg) + sizeof(struct voice_svc_register))) { - ret = process_reg_cmd( - (struct voice_svc_register *)data->payload, prtd); + register_data = + (struct voice_svc_register *)data->payload; + if (register_data == NULL) { + pr_err("%s: register data is NULL", __func__); + ret = -EINVAL; + goto done; + } + ret = process_reg_cmd(register_data, prtd); if (!ret) ret = count; } else { - pr_err("%s: invalid payload size\n", __func__); + pr_err("%s: invalid data payload size for register command\n", + __func__); ret = -EINVAL; goto done; } @@ -430,19 +440,40 @@ static ssize_t voice_svc_write(struct file *file, const char __user *buf, /* * Check that count reflects the expected size to ensure * sufficient memory was allocated. Since voice_svc_cmd_request - * has a variable size, check the minimum value count must be. + * has a variable size, check the minimum value count must be to + * parse the message request then check the minimum size to hold + * the payload of the message request. */ if (count >= (sizeof(struct voice_svc_write_msg) + sizeof(struct voice_svc_cmd_request))) { - ret = voice_svc_send_req( - (struct voice_svc_cmd_request *)data->payload, prtd); - if (!ret) - ret = count; - } else { - pr_err("%s: invalid payload size\n", __func__); - ret = -EINVAL; - goto done; - } + request_data = + (struct voice_svc_cmd_request *)data->payload; + if (request_data == NULL) { + pr_err("%s: request data is NULL", __func__); + ret = -EINVAL; + goto done; + } + + request_payload_size = request_data->payload_size; + + if (count >= (sizeof(struct voice_svc_write_msg) + + sizeof(struct voice_svc_cmd_request) + + request_payload_size)) { + ret = voice_svc_send_req(request_data, prtd); + if (!ret) + ret = count; + } else { + pr_err("%s: invalid request payload size\n", + __func__); + ret = -EINVAL; + goto done; + } + } else { + pr_err("%s: invalid data payload size for request command\n", + __func__); + ret = -EINVAL; + goto done; + } break; default: pr_debug("%s: Invalid command: %u\n", __func__, cmd); diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c index d6853e4bea72..0e7bf13c192b 100644 --- a/drivers/soc/qcom/qmi_interface.c +++ b/drivers/soc/qcom/qmi_interface.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -101,6 +101,7 @@ struct elem_info qmi_response_type_v01_ei[] = { .ei_array = NULL, }, }; +EXPORT_SYMBOL(qmi_response_type_v01_ei); struct elem_info qmi_error_resp_type_v01_ei[] = { { diff --git a/drivers/soc/qcom/qpnp-haptic.c b/drivers/soc/qcom/qpnp-haptic.c index 39070561d7e4..c86eebcd390f 100644 --- a/drivers/soc/qcom/qpnp-haptic.c +++ b/drivers/soc/qcom/qpnp-haptic.c @@ -10,6 +10,8 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) "haptic: %s: " fmt, __func__ + #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -25,21 +27,21 @@ #include <linux/qpnp/pwm.h> #include <linux/err.h> #include <linux/delay.h> +#include <linux/log2.h> +#include <linux/qpnp-misc.h> #include <linux/qpnp/qpnp-haptic.h> +#include <linux/qpnp/qpnp-revid.h> #include "../../staging/android/timed_output.h" -#define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \ - IRQF_TRIGGER_FALLING | \ - IRQF_ONESHOT) - #define QPNP_HAP_STATUS(b) (b + 0x0A) #define QPNP_HAP_LRA_AUTO_RES_LO(b) (b + 0x0B) #define QPNP_HAP_LRA_AUTO_RES_HI(b) (b + 0x0C) #define QPNP_HAP_EN_CTL_REG(b) (b + 0x46) #define QPNP_HAP_EN_CTL2_REG(b) (b + 0x48) -#define QPNP_HAP_ACT_TYPE_REG(b) (b + 0x4C) -#define QPNP_HAP_WAV_SHAPE_REG(b) (b + 0x4D) -#define QPNP_HAP_PLAY_MODE_REG(b) (b + 0x4E) +#define QPNP_HAP_AUTO_RES_CTRL(b) (b + 0x4B) +#define QPNP_HAP_CFG1_REG(b) (b + 0x4C) +#define QPNP_HAP_CFG2_REG(b) (b + 0x4D) +#define QPNP_HAP_SEL_REG(b) (b + 0x4E) #define QPNP_HAP_LRA_AUTO_RES_REG(b) (b + 0x4F) #define QPNP_HAP_VMAX_REG(b) (b + 0x51) #define QPNP_HAP_ILIM_REG(b) (b + 0x52) @@ -59,37 +61,48 @@ #define QPNP_HAP_TEST2_REG(b) (b + 0xE3) #define QPNP_HAP_STATUS_BUSY 0x02 -#define QPNP_HAP_ACT_TYPE_MASK 0xFE +#define QPNP_HAP_ACT_TYPE_MASK BIT(0) #define QPNP_HAP_LRA 0x0 #define QPNP_HAP_ERM 0x1 -#define QPNP_HAP_AUTO_RES_MODE_MASK 0x8F +#define QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT BIT(3) +#define QPNP_HAP_AUTO_RES_MODE_MASK GENMASK(6, 4) #define QPNP_HAP_AUTO_RES_MODE_SHIFT 4 -#define QPNP_HAP_LRA_HIGH_Z_MASK 0xF3 -#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 -#define QPNP_HAP_LRA_RES_CAL_PER_MASK 0xFC -#define QPNP_HAP_RES_CAL_PERIOD_MIN 4 -#define QPNP_HAP_RES_CAL_PERIOD_MAX 32 -#define QPNP_HAP_PLAY_MODE_MASK 0xCF -#define QPNP_HAP_PLAY_MODE_SHFT 4 -#define QPNP_HAP_VMAX_MASK 0xC1 +#define QPNP_HAP_PM660_AUTO_RES_MODE_BIT BIT(7) +#define QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT 7 +#define QPNP_HAP_PM660_CALIBRATE_DURATION_MASK GENMASK(6, 5) +#define QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT 5 +#define QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT BIT(4) +#define QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT 4 +#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT BIT(3) +#define QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT 3 +#define QPNP_HAP_PM660_LRA_ZXD_CAL_PERIOD_BIT GENMASK(2, 0) +#define QPNP_HAP_LRA_HIGH_Z_MASK GENMASK(3, 2) +#define QPNP_HAP_LRA_HIGH_Z_SHIFT 2 +#define QPNP_HAP_LRA_RES_CAL_PER_MASK GENMASK(1, 0) +#define QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK GENMASK(2, 0) +#define QPNP_HAP_RES_CAL_PERIOD_MIN 4 +#define QPNP_HAP_RES_CAL_PERIOD_MAX 32 +#define QPNP_HAP_PM660_RES_CAL_PERIOD_MAX 256 +#define QPNP_HAP_WF_SOURCE_MASK GENMASK(5, 4) +#define QPNP_HAP_WF_SOURCE_SHIFT 4 +#define QPNP_HAP_VMAX_MASK GENMASK(5, 1) #define QPNP_HAP_VMAX_SHIFT 1 #define QPNP_HAP_VMAX_MIN_MV 116 #define QPNP_HAP_VMAX_MAX_MV 3596 -#define QPNP_HAP_ILIM_MASK 0xFE +#define QPNP_HAP_ILIM_MASK BIT(0) #define QPNP_HAP_ILIM_MIN_MV 400 #define QPNP_HAP_ILIM_MAX_MV 800 -#define QPNP_HAP_SC_DEB_MASK 0xF8 -#define QPNP_HAP_SC_DEB_SUB 2 +#define QPNP_HAP_SC_DEB_MASK GENMASK(2, 0) #define QPNP_HAP_SC_DEB_CYCLES_MIN 0 #define QPNP_HAP_DEF_SC_DEB_CYCLES 8 #define QPNP_HAP_SC_DEB_CYCLES_MAX 32 #define QPNP_HAP_SC_CLR 1 -#define QPNP_HAP_INT_PWM_MASK 0xFC +#define QPNP_HAP_INT_PWM_MASK GENMASK(1, 0) #define QPNP_HAP_INT_PWM_FREQ_253_KHZ 253 #define QPNP_HAP_INT_PWM_FREQ_505_KHZ 505 #define QPNP_HAP_INT_PWM_FREQ_739_KHZ 739 #define QPNP_HAP_INT_PWM_FREQ_1076_KHZ 1076 -#define QPNP_HAP_WAV_SHAPE_MASK 0xFE +#define QPNP_HAP_WAV_SHAPE_MASK BIT(0) #define QPNP_HAP_RATE_CFG1_MASK 0xFF #define QPNP_HAP_RATE_CFG2_MASK 0xF0 #define QPNP_HAP_RATE_CFG2_SHFT 8 @@ -97,9 +110,9 @@ #define QPNP_HAP_WAV_PLAY_RATE_US_MIN 0 #define QPNP_HAP_DEF_WAVE_PLAY_RATE_US 5715 #define QPNP_HAP_WAV_PLAY_RATE_US_MAX 20475 -#define QPNP_HAP_WAV_REP_MASK 0x8F -#define QPNP_HAP_WAV_S_REP_MASK 0xFC -#define QPNP_HAP_WAV_REP_SHFT 4 +#define QPNP_HAP_WAV_REP_MASK GENMASK(6, 4) +#define QPNP_HAP_WAV_S_REP_MASK GENMASK(1, 0) +#define QPNP_HAP_WAV_REP_SHIFT 4 #define QPNP_HAP_WAV_REP_MIN 1 #define QPNP_HAP_WAV_REP_MAX 128 #define QPNP_HAP_WAV_S_REP_MIN 1 @@ -107,13 +120,13 @@ #define QPNP_HAP_BRAKE_PAT_MASK 0x3 #define QPNP_HAP_ILIM_MIN_MA 400 #define QPNP_HAP_ILIM_MAX_MA 800 -#define QPNP_HAP_EXT_PWM_MASK 0xFC +#define QPNP_HAP_EXT_PWM_MASK GENMASK(1, 0) #define QPNP_HAP_EXT_PWM_FREQ_25_KHZ 25 #define QPNP_HAP_EXT_PWM_FREQ_50_KHZ 50 #define QPNP_HAP_EXT_PWM_FREQ_75_KHZ 75 #define QPNP_HAP_EXT_PWM_FREQ_100_KHZ 100 #define PWM_MAX_DTEST_LINES 4 -#define QPNP_HAP_EXT_PWM_DTEST_MASK 0x0F +#define QPNP_HAP_EXT_PWM_DTEST_MASK GENMASK(6, 4) #define QPNP_HAP_EXT_PWM_DTEST_SHFT 4 #define QPNP_HAP_EXT_PWM_PEAK_DATA 0x7F #define QPNP_HAP_EXT_PWM_HALF_DUTY 50 @@ -126,11 +139,9 @@ #define QPNP_HAP_BRAKE_PAT_LEN 4 #define QPNP_HAP_PLAY_EN 0x80 #define QPNP_HAP_EN 0x80 -#define QPNP_HAP_BRAKE_MASK 0xFE -#define QPNP_HAP_TEST2_AUTO_RES_MASK 0x7F -#define QPNP_HAP_SEC_UNLOCK 0xA5 -#define AUTO_RES_ENABLE 0x80 -#define AUTO_RES_DISABLE 0x00 +#define QPNP_HAP_BRAKE_MASK BIT(0) +#define QPNP_HAP_AUTO_RES_MASK BIT(7) +#define AUTO_RES_ENABLE BIT(7) #define AUTO_RES_ERR_BIT 0x10 #define SC_FOUND_BIT 0x08 #define SC_MAX_DURATION 5 @@ -141,19 +152,25 @@ #define QPNP_HAP_CYCLS 5 #define QPNP_TEST_TIMER_MS 5 -#define AUTO_RES_ENABLE_TIMEOUT 20000 -#define AUTO_RES_ERR_CAPTURE_RES 5 -#define AUTO_RES_ERR_MAX 15 - -#define MISC_TRIM_ERROR_RC19P2_CLK 0x09F5 -#define MISC_SEC_ACCESS 0x09D0 -#define MISC_SEC_UNLOCK 0xA5 -#define PMI8950_MISC_SID 2 - -#define POLL_TIME_AUTO_RES_ERR_NS (5 * NSEC_PER_MSEC) - -#define LRA_POS_FREQ_COUNT 6 -int lra_play_rate_code[LRA_POS_FREQ_COUNT]; +#define QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN 20000 +#define POLL_TIME_AUTO_RES_ERR_NS (20 * NSEC_PER_MSEC) + +#define MAX_POSITIVE_VARIATION_LRA_FREQ 30 +#define MAX_NEGATIVE_VARIATION_LRA_FREQ -30 +#define FREQ_VARIATION_STEP 5 +#define AUTO_RES_ERROR_CAPTURE_RES 5 +#define AUTO_RES_ERROR_MAX 30 +#define ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE \ + ((MAX_POSITIVE_VARIATION_LRA_FREQ - MAX_NEGATIVE_VARIATION_LRA_FREQ) \ + / FREQ_VARIATION_STEP) +#define LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent) \ + (hap->init_drive_period_code = (hap->init_drive_period_code * \ + (1000 + rc_clk_err_percent_x10)) / 1000) +#define LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent) \ + (hap->init_drive_period_code = (hap->init_drive_period_code * \ + (1000 - rc_clk_err_percent_x10)) / 1000) + +u32 adjusted_lra_play_rate_code[ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE]; /* haptic debug register set */ static u8 qpnp_hap_dbg_regs[] = { @@ -210,9 +227,14 @@ enum qpnp_hap_auto_res_mode { QPNP_HAP_AUTO_RES_ZXD_EOP, }; +enum qpnp_hap_pm660_auto_res_mode { + QPNP_HAP_PM660_AUTO_RES_ZXD, + QPNP_HAP_PM660_AUTO_RES_QWD, +}; + /* high Z option lines */ enum qpnp_hap_high_z { - QPNP_HAP_LRA_HIGH_Z_NONE, + QPNP_HAP_LRA_HIGH_Z_NONE, /* opt0 for PM660 */ QPNP_HAP_LRA_HIGH_Z_OPT1, QPNP_HAP_LRA_HIGH_Z_OPT2, QPNP_HAP_LRA_HIGH_Z_OPT3, @@ -226,6 +248,11 @@ enum qpnp_hap_mode { QPNP_HAP_PWM, }; +/* status flags */ +enum qpnp_hap_status { + AUTO_RESONANCE_ENABLED = BIT(0), +}; + /* pwm channel info */ struct qpnp_pwm_info { struct pwm_device *pwm_dev; @@ -241,15 +268,26 @@ struct qpnp_pwm_info { * @ auto_res_err_poll_timer - hrtimer for auto-resonance error * @ timed_dev - timed output device * @ work - worker - * @ auto_res_err_work - correct auto resonance error * @ sc_work - worker to handle short circuit condition * @ pwm_info - pwm info * @ lock - mutex lock * @ wf_lock - mutex lock for waveform + * @ init_drive_period_code - the initial lra drive period code + * @ drive_period_code_max_limit_percent_variation - maximum limit of + percentage variation of drive period code + * @ drive_period_code_min_limit_percent_variation - minimum limit og + percentage variation of drive period code + * @ drive_period_code_max_limit - calculated drive period code with + percentage variation on the higher side. + * @ drive_period_code_min_limit - calculated drive period code with + percentage variation on the lower side + * @ lra_res_cal_period - LRA resonance calibration period * @ play_mode - play mode * @ auto_res_mode - auto resonace mode * @ lra_high_z - high z option line * @ timeout_ms - max timeout in ms + * @ time_required_to_generate_back_emf_us - the time required for sufficient + back-emf to be generated for auto resonance to be successful * @ vmax_mv - max voltage in mv * @ ilim_ma - limiting current in ma * @ sc_deb_cycles - short circuit debounce cycles @@ -260,6 +298,7 @@ struct qpnp_pwm_info { * @ wave_s_rep_cnt - waveform sample repeat count * @ play_irq - irq for play * @ sc_irq - irq for short circuit + * @ status_flags - status * @ base - base address * @ act_type - actuator type * @ wave_shape - waveform shape @@ -270,16 +309,18 @@ struct qpnp_pwm_info { * @ reg_play - play register * @ lra_res_cal_period - period for resonance calibration * @ sc_duration - counter to determine the duration of short circuit condition + * @ lra_hw_auto_resonance - enable hardware auto resonance * @ state - current state of haptics - * @ use_play_irq - play irq usage state - * @ use_sc_irq - short circuit irq usage state * @ wf_update - waveform update flag * @ pwm_cfg_state - pwm mode configuration state * @ buffer_cfg_state - buffer mode configuration state * @ en_brake - brake state * @ sup_brake_pat - support custom brake pattern * @ correct_lra_drive_freq - correct LRA Drive Frequency - * @ misc_trim_error_rc19p2_clk_reg_present - if MISC Trim Error reg is present + * @ misc_clk_trim_error_reg - MISC clock trim error register if present + * @ clk_trim_error_code - MISC clock trim error code + * @ perform_lra_auto_resonance_search - whether lra auto resonance search + * algorithm should be performed or not. */ struct qpnp_hap { struct platform_device *pdev; @@ -289,18 +330,22 @@ struct qpnp_hap { struct hrtimer auto_res_err_poll_timer; struct timed_output_dev timed_dev; struct work_struct work; - struct work_struct auto_res_err_work; struct delayed_work sc_work; struct hrtimer hap_test_timer; struct work_struct test_work; struct qpnp_pwm_info pwm_info; struct mutex lock; struct mutex wf_lock; + spinlock_t bus_lock; struct completion completion; enum qpnp_hap_mode play_mode; - enum qpnp_hap_auto_res_mode auto_res_mode; enum qpnp_hap_high_z lra_high_z; + int lra_qwd_drive_duration; + int calibrate_at_eop; + u32 misc_clk_trim_error_reg; + u32 init_drive_period_code; u32 timeout_ms; + u32 time_required_to_generate_back_emf_us; u32 vmax_mv; u32 ilim_ma; u32 sc_deb_cycles; @@ -311,20 +356,28 @@ struct qpnp_hap { u32 wave_s_rep_cnt; u32 play_irq; u32 sc_irq; + u32 status_flags; u16 base; + u16 drive_period_code_max_limit; + u16 drive_period_code_min_limit; + u16 lra_res_cal_period; + u8 drive_period_code_max_limit_percent_variation; + u8 drive_period_code_min_limit_percent_variation; u8 act_type; u8 wave_shape; - u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; - u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; - u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; + u8 wave_samp[QPNP_HAP_WAV_SAMP_LEN]; + u8 shadow_wave_samp[QPNP_HAP_WAV_SAMP_LEN]; + u8 brake_pat[QPNP_HAP_BRAKE_PAT_LEN]; u8 reg_en_ctl; u8 reg_play; - u8 lra_res_cal_period; u8 sc_duration; u8 ext_pwm_dtest_line; + u8 pmic_subtype; + u8 auto_res_mode; + u8 clk_trim_error_code; + bool lra_hw_auto_resonance; + bool vcc_pon_enabled; bool state; - bool use_play_irq; - bool use_sc_irq; bool manage_pon_supply; bool wf_update; bool pwm_cfg_state; @@ -332,66 +385,98 @@ struct qpnp_hap { bool en_brake; bool sup_brake_pat; bool correct_lra_drive_freq; - bool misc_trim_error_rc19p2_clk_reg_present; + bool perform_lra_auto_resonance_search; }; static struct qpnp_hap *ghap; /* helper to read a pmic register */ -static int qpnp_hap_read_reg(struct qpnp_hap *hap, u8 *data, u16 addr) +static int qpnp_hap_read_reg(struct qpnp_hap *hap, u16 addr, u8 *val) { int rc; - uint val; + uint tmp; - rc = regmap_read(hap->regmap, addr, &val); + rc = regmap_read(hap->regmap, addr, &tmp); if (rc < 0) - dev_err(&hap->pdev->dev, - "Error reading address: %X - ret %X\n", addr, rc); - *data = (u8)val; + pr_err("Error reading address: %X - ret %X\n", addr, rc); + *val = (u8)tmp; return rc; } /* helper to write a pmic register */ -static int qpnp_hap_write_reg(struct qpnp_hap *hap, u8 *data, u16 addr) +static int qpnp_hap_write_reg(struct qpnp_hap *hap, u16 addr, u8 val) { + unsigned long flags; int rc; - rc = regmap_write(hap->regmap, addr, *data); + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_write(hap->regmap, addr, val); if (rc < 0) - dev_err(&hap->pdev->dev, - "Error writing address: %X - ret %X\n", addr, rc); + pr_err("Error writing address: %X - ret %X\n", addr, rc); - dev_dbg(&hap->pdev->dev, "write: HAP_0x%x = 0x%x\n", addr, *data); + spin_unlock_irqrestore(&hap->bus_lock, flags); + if (!rc) + pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); return rc; } /* helper to access secure registers */ -static int qpnp_hap_sec_access(struct qpnp_hap *hap) +#define QPNP_HAP_SEC_UNLOCK 0xA5 +static int qpnp_hap_sec_masked_write_reg(struct qpnp_hap *hap, u16 addr, + u8 mask, u8 val) { + unsigned long flags; int rc; - u8 reg = QPNP_HAP_SEC_UNLOCK; + u8 tmp = QPNP_HAP_SEC_UNLOCK; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_SEC_ACCESS_REG(hap->base)); - if (rc) - return rc; + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_write(hap->regmap, QPNP_HAP_SEC_ACCESS_REG(hap->base), tmp); + if (rc < 0) { + pr_err("Error writing sec_code - ret %X\n", rc); + goto out; + } - return 0; + rc = regmap_update_bits(hap->regmap, addr, mask, val); + if (rc < 0) + pr_err("Error writing address: %X - ret %X\n", addr, rc); + +out: + spin_unlock_irqrestore(&hap->bus_lock, flags); + if (!rc) + pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); + return rc; +} + +static int qpnp_hap_masked_write_reg(struct qpnp_hap *hap, u16 addr, u8 mask, + u8 val) +{ + unsigned long flags; + int rc; + + spin_lock_irqsave(&hap->bus_lock, flags); + rc = regmap_update_bits(hap->regmap, addr, mask, val); + if (rc < 0) + pr_err("Error writing address: %X - ret %X\n", addr, rc); + + spin_unlock_irqrestore(&hap->bus_lock, flags); + if (!rc) + pr_debug("wrote: HAP_0x%x = 0x%x\n", addr, val); + return rc; } static void qpnp_handle_sc_irq(struct work_struct *work) { struct qpnp_hap *hap = container_of(work, struct qpnp_hap, sc_work.work); - u8 val, reg; + u8 val; - qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); + qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); /* clear short circuit register */ if (val & SC_FOUND_BIT) { hap->sc_duration++; - reg = QPNP_HAP_SC_CLR; - qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_CLR_REG(hap->base)); + val = QPNP_HAP_SC_CLR; + qpnp_hap_write_reg(hap, QPNP_HAP_SC_CLR_REG(hap->base), val); } } @@ -409,10 +494,10 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) unsigned long sleep_time = QPNP_HAP_CYCLS * hap->wave_play_rate_us; - rc = qpnp_hap_read_reg(hap, &val, - QPNP_HAP_STATUS(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), + &val); - dev_dbg(&hap->pdev->dev, "HAP_STATUS=0x%x\n", val); + pr_debug("HAP_STATUS=0x%x\n", val); /* wait for QPNP_HAP_CYCLS cycles of play rate */ if (val & QPNP_HAP_STATUS_BUSY) { @@ -425,14 +510,12 @@ static int qpnp_hap_mod_enable(struct qpnp_hap *hap, int on) } if (i >= QPNP_HAP_MAX_RETRIES) - dev_dbg(&hap->pdev->dev, - "Haptics Busy. Force disable\n"); + pr_debug("Haptics Busy. Force disable\n"); val &= ~QPNP_HAP_EN; } - rc = qpnp_hap_write_reg(hap, &val, - QPNP_HAP_EN_CTL_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), val); if (rc < 0) return rc; @@ -452,8 +535,7 @@ static int qpnp_hap_play(struct qpnp_hap *hap, int on) else val &= ~QPNP_HAP_PLAY_EN; - rc = qpnp_hap_write_reg(hap, &val, - QPNP_HAP_PLAY_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_PLAY_REG(hap->base), val); if (rc < 0) return rc; @@ -473,7 +555,7 @@ static ssize_t qpnp_hap_dump_regs_show(struct device *dev, u8 val; for (i = 0; i < ARRAY_SIZE(qpnp_hap_dbg_regs); i++) { - qpnp_hap_read_reg(hap, &val, hap->base + qpnp_hap_dbg_regs[i]); + qpnp_hap_read_reg(hap, hap->base + qpnp_hap_dbg_regs[i], &val); count += snprintf(buf + count, PAGE_SIZE - count, "qpnp_haptics: REG_0x%x = 0x%x\n", hap->base + qpnp_hap_dbg_regs[i], @@ -491,15 +573,15 @@ static irqreturn_t qpnp_hap_play_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int i, rc; - u8 reg; + u8 val; mutex_lock(&hap->wf_lock); /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN && hap->wf_update; i++) { - reg = hap->wave_samp[i] = hap->shadow_wave_samp[i]; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); + val = hap->wave_samp[i] = hap->shadow_wave_samp[i]; + rc = qpnp_hap_write_reg(hap, + QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val); if (rc) goto unlock; } @@ -516,13 +598,12 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) { struct qpnp_hap *hap = _hap; int rc; - u8 disable_haptics = 0x00; u8 val; - dev_dbg(&hap->pdev->dev, "Short circuit detected\n"); + pr_debug("Short circuit detected\n"); if (hap->sc_duration < SC_MAX_DURATION) { - qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); + qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); if (val & SC_FOUND_BIT) schedule_delayed_work(&hap->sc_work, QPNP_HAP_SC_IRQ_STATUS_DELAY); @@ -532,10 +613,10 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). */ - rc = qpnp_hap_write_reg(hap, &disable_haptics, - QPNP_HAP_EN_CTL_REG(hap->base)); - dev_err(&hap->pdev->dev, - "Haptics disabled permanently due to short circuit\n"); + val = 0; + rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), + val); + pr_err("Haptics disabled permanently due to short circuit\n"); } return IRQ_HANDLED; @@ -544,8 +625,8 @@ static irqreturn_t qpnp_hap_sc_irq(int irq, void *_hap) /* configuration api for buffer mode */ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, i, temp; + u8 val = 0; + int rc, i; /* Configure the WAVE_REPEAT register */ if (hap->wave_rep_cnt < QPNP_HAP_WAV_REP_MIN) @@ -558,44 +639,22 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) else if (hap->wave_s_rep_cnt > QPNP_HAP_WAV_S_REP_MAX) hap->wave_s_rep_cnt = QPNP_HAP_WAV_S_REP_MAX; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_REP_MASK; - temp = fls(hap->wave_rep_cnt) - 1; - reg |= (temp << QPNP_HAP_WAV_REP_SHFT); - reg &= QPNP_HAP_WAV_S_REP_MASK; - temp = fls(hap->wave_s_rep_cnt) - 1; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); + val = ilog2(hap->wave_rep_cnt) << QPNP_HAP_WAV_REP_SHIFT | + ilog2(hap->wave_s_rep_cnt); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), + QPNP_HAP_WAV_REP_MASK | QPNP_HAP_WAV_S_REP_MASK, val); if (rc) return rc; /* Configure WAVE_SAMPLE1 to WAVE_SAMPLE8 register */ - for (i = 0, reg = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { - reg = hap->wave_samp[i]; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_S_REG_BASE(hap->base) + i); + for (i = 0, val = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) { + val = hap->wave_samp[i]; + rc = qpnp_hap_write_reg(hap, + QPNP_HAP_WAV_S_REG_BASE(hap->base) + i, val); if (rc) return rc; } - /* setup play irq */ - if (hap->use_play_irq) { - rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq, - NULL, qpnp_hap_play_irq, - QPNP_IRQ_FLAGS, - "qpnp_play_irq", hap); - if (rc < 0) { - dev_err(&hap->pdev->dev, - "Unable to request play(%d) IRQ(err:%d)\n", - hap->play_irq, rc); - return rc; - } - } - hap->buffer_cfg_state = true; return 0; } @@ -603,59 +662,41 @@ static int qpnp_hap_buffer_config(struct qpnp_hap *hap) /* configuration api for pwm */ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; /* Configure the EXTERNAL_PWM register */ if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_25_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_25_KHZ; - temp = 0; + val = 0; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_50_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_50_KHZ; - temp = 1; + val = 1; } else if (hap->ext_pwm_freq_khz <= QPNP_HAP_EXT_PWM_FREQ_75_KHZ) { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_75_KHZ; - temp = 2; + val = 2; } else { hap->ext_pwm_freq_khz = QPNP_HAP_EXT_PWM_FREQ_100_KHZ; - temp = 3; + val = 3; } - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_EXT_PWM_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_EXT_PWM_MASK; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_EXT_PWM_REG(hap->base)); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EXT_PWM_REG(hap->base), + QPNP_HAP_EXT_PWM_MASK, val); if (rc) return rc; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_TEST2_REG(hap->base)); - if (rc) - return rc; if (!hap->ext_pwm_dtest_line || hap->ext_pwm_dtest_line > PWM_MAX_DTEST_LINES) { - dev_err(&hap->pdev->dev, "invalid dtest line\n"); + pr_err("invalid dtest line\n"); return -EINVAL; } /* disable auto res for PWM mode */ - reg &= QPNP_HAP_EXT_PWM_DTEST_MASK; - temp = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT; - reg |= temp; - - /* TEST2 is a secure access register */ - rc = qpnp_hap_sec_access(hap); - if (rc) - return rc; - - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_TEST2_REG(hap->base)); + val = hap->ext_pwm_dtest_line << QPNP_HAP_EXT_PWM_DTEST_SHFT; + rc = qpnp_hap_sec_masked_write_reg(hap, QPNP_HAP_TEST2_REG(hap->base), + QPNP_HAP_EXT_PWM_DTEST_MASK | QPNP_HAP_AUTO_RES_MASK, val); if (rc) return rc; @@ -663,7 +704,7 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) hap->pwm_info.duty_us * NSEC_PER_USEC, hap->pwm_info.period_us * NSEC_PER_USEC); if (rc < 0) { - dev_err(&hap->pdev->dev, "hap pwm config failed\n"); + pr_err("hap pwm config failed\n"); pwm_free(hap->pwm_info.pwm_dev); return -ENODEV; } @@ -673,72 +714,180 @@ static int qpnp_hap_pwm_config(struct qpnp_hap *hap) return 0; } +static int qpnp_hap_lra_auto_res_config(struct qpnp_hap *hap) +{ + int rc; + u8 val, mask; + + /* disable auto resonance for ERM */ + if (hap->act_type == QPNP_HAP_ERM) { + val = 0x00; + rc = qpnp_hap_write_reg(hap, + QPNP_HAP_LRA_AUTO_RES_REG(hap->base), val); + return rc; + } + + if (hap->lra_hw_auto_resonance) { + rc = qpnp_hap_masked_write_reg(hap, + QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT, + QPNP_HAP_AUTO_RES_CTRL(hap->base), + QPNP_HAP_PM660_HW_AUTO_RES_MODE_BIT); + if (rc) + return rc; + } + + if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; + + if (hap->pmic_subtype == PM660_SUBTYPE) { + if (hap->lra_res_cal_period > + QPNP_HAP_PM660_RES_CAL_PERIOD_MAX) + hap->lra_res_cal_period = + QPNP_HAP_PM660_RES_CAL_PERIOD_MAX; + + if (hap->auto_res_mode == QPNP_HAP_PM660_AUTO_RES_QWD) + hap->lra_res_cal_period = 0; + } else { + if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; + } + + val = mask = 0; + if (hap->lra_res_cal_period) + val = ilog2(hap->lra_res_cal_period / + QPNP_HAP_RES_CAL_PERIOD_MIN); + + if (hap->pmic_subtype == PM660_SUBTYPE) { + val |= hap->auto_res_mode << + QPNP_HAP_PM660_AUTO_RES_MODE_SHIFT; + mask = QPNP_HAP_PM660_AUTO_RES_MODE_BIT; + val |= hap->lra_high_z << + QPNP_HAP_PM660_CALIBRATE_DURATION_SHIFT; + mask |= QPNP_HAP_PM660_CALIBRATE_DURATION_MASK; + if (hap->lra_qwd_drive_duration != -EINVAL) { + val |= hap->lra_qwd_drive_duration << + QPNP_HAP_PM660_QWD_DRIVE_DURATION_SHIFT; + mask |= QPNP_HAP_PM660_QWD_DRIVE_DURATION_BIT; + } + if (hap->calibrate_at_eop != -EINVAL) { + val |= hap->calibrate_at_eop << + QPNP_HAP_PM660_CALIBRATE_AT_EOP_SHIFT; + mask |= QPNP_HAP_PM660_CALIBRATE_AT_EOP_BIT; + } + mask |= QPNP_HAP_PM660_LRA_RES_CAL_PER_MASK; + } else { + val |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT); + val |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); + mask = QPNP_HAP_AUTO_RES_MODE_MASK | QPNP_HAP_LRA_HIGH_Z_MASK | + QPNP_HAP_LRA_RES_CAL_PER_MASK; + } + + rc = qpnp_hap_masked_write_reg(hap, + QPNP_HAP_LRA_AUTO_RES_REG(hap->base), mask, val); + return rc; +} + /* configuration api for play mode */ static int qpnp_hap_play_mode_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_PLAY_MODE_MASK; - temp = hap->play_mode << QPNP_HAP_PLAY_MODE_SHFT; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PLAY_MODE_REG(hap->base)); - if (rc) - return rc; - return 0; + val = hap->play_mode << QPNP_HAP_WF_SOURCE_SHIFT; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SEL_REG(hap->base), + QPNP_HAP_WF_SOURCE_MASK, val); + return rc; } -/* configuration api for max volatge */ +/* configuration api for max voltage */ static int qpnp_hap_vmax_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; if (hap->vmax_mv < QPNP_HAP_VMAX_MIN_MV) hap->vmax_mv = QPNP_HAP_VMAX_MIN_MV; else if (hap->vmax_mv > QPNP_HAP_VMAX_MAX_MV) hap->vmax_mv = QPNP_HAP_VMAX_MAX_MV; - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_VMAX_MASK; - temp = hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV; - reg |= (temp << QPNP_HAP_VMAX_SHIFT); - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_VMAX_REG(hap->base)); - if (rc) - return rc; + val = (hap->vmax_mv / QPNP_HAP_VMAX_MIN_MV) << QPNP_HAP_VMAX_SHIFT; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_VMAX_REG(hap->base), + QPNP_HAP_VMAX_MASK, val); + return rc; +} - return 0; +/* configuration api for ilim */ +static int qpnp_hap_ilim_config(struct qpnp_hap *hap) +{ + u8 val = 0; + int rc; + + if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) + hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; + else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) + hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; + + val = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) - 1; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_ILIM_REG(hap->base), + QPNP_HAP_ILIM_MASK, val); + return rc; } /* configuration api for short circuit debounce */ static int qpnp_hap_sc_deb_config(struct qpnp_hap *hap) { - u8 reg = 0; - int rc, temp; + u8 val = 0; + int rc; if (hap->sc_deb_cycles < QPNP_HAP_SC_DEB_CYCLES_MIN) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MIN; else if (hap->sc_deb_cycles > QPNP_HAP_SC_DEB_CYCLES_MAX) hap->sc_deb_cycles = QPNP_HAP_SC_DEB_CYCLES_MAX; - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_SC_DEB_MASK; - if (hap->sc_deb_cycles) { - temp = fls(hap->sc_deb_cycles) - 1; - reg |= temp - QPNP_HAP_SC_DEB_SUB; + if (hap->sc_deb_cycles != QPNP_HAP_SC_DEB_CYCLES_MIN) + val = ilog2(hap->sc_deb_cycles / + QPNP_HAP_DEF_SC_DEB_CYCLES) + 1; + else + val = QPNP_HAP_SC_DEB_CYCLES_MIN; + + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_SC_DEB_REG(hap->base), + QPNP_HAP_SC_DEB_MASK, val); + + return rc; +} + +static int qpnp_hap_int_pwm_config(struct qpnp_hap *hap) +{ + int rc; + u8 val; + + if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { + if (hap->pmic_subtype == PM660_SUBTYPE) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; + val = 1; + } else { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; + val = 0; + } + } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; + val = 1; + } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; + val = 2; + } else { + hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; + val = 3; } - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_SC_DEB_REG(hap->base)); + + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_INT_PWM_REG(hap->base), + QPNP_HAP_INT_PWM_MASK, val); if (rc) return rc; - return 0; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_PWM_CAP_REG(hap->base), + QPNP_HAP_INT_PWM_MASK, val); + return rc; } /* DT parsing api for buffer mode */ @@ -755,7 +904,7 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_rep_cnt = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read rep cnt\n"); + pr_err("Unable to read rep cnt\n"); return rc; } @@ -765,30 +914,20 @@ static int qpnp_hap_parse_buffer_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_s_rep_cnt = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read samp rep cnt\n"); + pr_err("Unable to read samp rep cnt\n"); return rc; } prop = of_find_property(pdev->dev.of_node, "qcom,wave-samples", &temp); if (!prop || temp != QPNP_HAP_WAV_SAMP_LEN) { - dev_err(&pdev->dev, "Invalid wave samples, use default"); + pr_err("Invalid wave samples, use default"); for (i = 0; i < QPNP_HAP_WAV_SAMP_LEN; i++) hap->wave_samp[i] = QPNP_HAP_WAV_SAMP_MAX; } else { memcpy(hap->wave_samp, prop->value, QPNP_HAP_WAV_SAMP_LEN); } - hap->use_play_irq = of_property_read_bool(pdev->dev.of_node, - "qcom,use-play-irq"); - if (hap->use_play_irq) { - hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq"); - if (hap->play_irq < 0) { - dev_err(&pdev->dev, "Unable to get play irq\n"); - return hap->play_irq; - } - } - return 0; } @@ -805,7 +944,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap) if (!rc) { hap->ext_pwm_freq_khz = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read ext pwm freq\n"); + pr_err("Unable to read ext pwm freq\n"); return rc; } @@ -820,7 +959,7 @@ static int qpnp_hap_parse_pwm_dt(struct qpnp_hap *hap) if (IS_ERR(hap->pwm_info.pwm_dev)) { rc = PTR_ERR(hap->pwm_info.pwm_dev); - dev_err(&pdev->dev, "Cannot get PWM device rc:(%d)\n", rc); + pr_err("Cannot get PWM device rc:(%d)\n", rc); hap->pwm_info.pwm_dev = NULL; return rc; } @@ -855,7 +994,7 @@ static ssize_t qpnp_hap_wf_samp_show(struct device *dev, char *buf, int index) timed_dev); if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { - dev_err(dev, "Invalid sample index(%d)\n", index); + pr_err("Invalid sample index(%d)\n", index); return -EINVAL; } @@ -921,7 +1060,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, int data, rc; if (index < 0 || index >= QPNP_HAP_WAV_SAMP_LEN) { - dev_err(dev, "Invalid sample index(%d)\n", index); + pr_err("Invalid sample index(%d)\n", index); return -EINVAL; } @@ -930,7 +1069,7 @@ static ssize_t qpnp_hap_wf_samp_store(struct device *dev, return rc; if (data < 0 || data > 0xff) { - dev_err(dev, "Invalid sample wf_%d (%d)\n", index, data); + pr_err("Invalid sample wf_%d (%d)\n", index, data); return -EINVAL; } @@ -1030,8 +1169,8 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev, struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); - int data, rc, temp; - u8 reg; + int data, rc; + u8 val; rc = kstrtoint(buf, 10, &data); if (rc) @@ -1042,19 +1181,11 @@ static ssize_t qpnp_hap_wf_rep_store(struct device *dev, else if (data > QPNP_HAP_WAV_REP_MAX) data = QPNP_HAP_WAV_REP_MAX; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_REP_MASK; - temp = fls(data) - 1; - reg |= (temp << QPNP_HAP_WAV_REP_SHFT); - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc) - return rc; - - hap->wave_rep_cnt = data; + val = ilog2(data) << QPNP_HAP_WAV_REP_SHIFT; + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), + QPNP_HAP_WAV_REP_MASK, val); + if (!rc) + hap->wave_rep_cnt = data; return count; } @@ -1077,8 +1208,8 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, struct timed_output_dev *timed_dev = dev_get_drvdata(dev); struct qpnp_hap *hap = container_of(timed_dev, struct qpnp_hap, timed_dev); - int data, rc, temp; - u8 reg; + int data, rc; + u8 val; rc = kstrtoint(buf, 10, &data); if (rc) @@ -1089,19 +1220,11 @@ static ssize_t qpnp_hap_wf_s_rep_store(struct device *dev, else if (data > QPNP_HAP_WAV_S_REP_MAX) data = QPNP_HAP_WAV_S_REP_MAX; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_S_REP_MASK; - temp = fls(data) - 1; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_REP_REG(hap->base)); - if (rc) - return rc; - - hap->wave_s_rep_cnt = data; + val = ilog2(hap->wave_s_rep_cnt); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_WAV_REP_REG(hap->base), + QPNP_HAP_WAV_S_REP_MASK, val); + if (!rc) + hap->wave_s_rep_cnt = data; return count; } @@ -1314,29 +1437,57 @@ static struct device_attribute qpnp_hap_attrs[] = { qpnp_hap_min_max_test_data_store), }; -static void calculate_lra_code(struct qpnp_hap *hap) +static int calculate_lra_code(struct qpnp_hap *hap) { - u8 play_rate_code_lo, play_rate_code_hi; - int play_rate_code, neg_idx = 0, pos_idx = LRA_POS_FREQ_COUNT-1; - int lra_init_freq, freq_variation, start_variation = AUTO_RES_ERR_MAX; + u8 lra_drive_period_code_lo = 0, lra_drive_period_code_hi = 0; + u32 lra_drive_period_code, lra_drive_frequency_hz, freq_variation; + u8 start_variation = AUTO_RES_ERROR_MAX, i; + u8 neg_idx = 0, pos_idx = ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE - 1; + int rc = 0; - qpnp_hap_read_reg(hap, &play_rate_code_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); - qpnp_hap_read_reg(hap, &play_rate_code_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), + &lra_drive_period_code_lo); + if (rc) { + pr_err("Error while reading RATE_CFG1 register\n"); + return rc; + } - play_rate_code = (play_rate_code_hi << 8) | (play_rate_code_lo & 0xff); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), + &lra_drive_period_code_hi); + if (rc) { + pr_err("Error while reading RATE_CFG2 register\n"); + return rc; + } + + if (!lra_drive_period_code_lo && !lra_drive_period_code_hi) { + pr_err("Unexpected Error: both RATE_CFG1 and RATE_CFG2 read 0\n"); + return -EINVAL; + } - lra_init_freq = 200000 / play_rate_code; + lra_drive_period_code = + (lra_drive_period_code_hi << 8) | (lra_drive_period_code_lo & 0xff); + lra_drive_frequency_hz = 200000 / lra_drive_period_code; - while (start_variation >= AUTO_RES_ERR_CAPTURE_RES) { - freq_variation = (lra_init_freq * start_variation) / 100; - lra_play_rate_code[neg_idx++] = 200000 / (lra_init_freq - - freq_variation); - lra_play_rate_code[pos_idx--] = 200000 / (lra_init_freq + - freq_variation); - start_variation -= AUTO_RES_ERR_CAPTURE_RES; + while (start_variation >= AUTO_RES_ERROR_CAPTURE_RES) { + freq_variation = + (lra_drive_frequency_hz * start_variation) / 100; + adjusted_lra_play_rate_code[neg_idx++] = + 200000 / (lra_drive_frequency_hz - freq_variation); + adjusted_lra_play_rate_code[pos_idx--] = + 200000 / (lra_drive_frequency_hz + freq_variation); + start_variation -= AUTO_RES_ERROR_CAPTURE_RES; } + + pr_debug("lra_drive_period_code_lo = 0x%x lra_drive_period_code_hi = 0x%x\n" + "lra_drive_period_code = 0x%x, lra_drive_frequency_hz = 0x%x\n" + "Calculated play rate code values are :\n", + lra_drive_period_code_lo, lra_drive_period_code_hi, + lra_drive_period_code, lra_drive_frequency_hz); + + for (i = 0; i < ADJUSTED_LRA_PLAY_RATE_CODE_ARRSIZE; ++i) + pr_debug(" 0x%x", adjusted_lra_play_rate_code[i]); + + return 0; } static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) @@ -1344,60 +1495,86 @@ static int qpnp_hap_auto_res_enable(struct qpnp_hap *hap, int enable) int rc = 0; u8 val; - rc = qpnp_hap_read_reg(hap, &val, QPNP_HAP_TEST2_REG(hap->base)); + val = enable ? AUTO_RES_ENABLE : 0; + if (hap->pmic_subtype == PM660_SUBTYPE) + rc = qpnp_hap_masked_write_reg(hap, + QPNP_HAP_AUTO_RES_CTRL(hap->base), + QPNP_HAP_AUTO_RES_MASK, val); + else + rc = qpnp_hap_sec_masked_write_reg(hap, + QPNP_HAP_TEST2_REG(hap->base), + QPNP_HAP_AUTO_RES_MASK, val); if (rc < 0) return rc; - val &= QPNP_HAP_TEST2_AUTO_RES_MASK; if (enable) - val |= AUTO_RES_ENABLE; + hap->status_flags |= AUTO_RESONANCE_ENABLED; else - val |= AUTO_RES_DISABLE; + hap->status_flags &= ~AUTO_RESONANCE_ENABLED; - /* TEST2 is a secure access register */ - rc = qpnp_hap_sec_access(hap); - if (rc) - return rc; - - rc = qpnp_hap_write_reg(hap, &val, QPNP_HAP_TEST2_REG(hap->base)); - if (rc) - return rc; - - return 0; + return rc; } static void update_lra_frequency(struct qpnp_hap *hap) { - u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0; + u8 lra_auto_res_lo = 0, lra_auto_res_hi = 0, val; + u32 play_rate_code; + int rc; + + qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_LO(hap->base), + &lra_auto_res_lo); + qpnp_hap_read_reg(hap, QPNP_HAP_LRA_AUTO_RES_HI(hap->base), + &lra_auto_res_hi); - qpnp_hap_read_reg(hap, &lra_auto_res_lo, - QPNP_HAP_LRA_AUTO_RES_LO(hap->base)); - qpnp_hap_read_reg(hap, &lra_auto_res_hi, - QPNP_HAP_LRA_AUTO_RES_HI(hap->base)); + play_rate_code = + (lra_auto_res_hi & 0xF0) << 4 | (lra_auto_res_lo & 0xFF); - if (lra_auto_res_lo && lra_auto_res_hi) { - qpnp_hap_write_reg(hap, &lra_auto_res_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + pr_debug("lra_auto_res_lo = 0x%x lra_auto_res_hi = 0x%x play_rate_code = 0x%x\n", + lra_auto_res_lo, lra_auto_res_hi, play_rate_code); - lra_auto_res_hi = lra_auto_res_hi >> 4; - qpnp_hap_write_reg(hap, &lra_auto_res_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_STATUS(hap->base), &val); + if (rc < 0) + return; + + /* + * If the drive period code read from AUTO_RES_LO and AUTO_RES_HI + * registers is more than the max limit percent variation or less + * than the min limit percent variation specified through DT, then + * auto-resonance is disabled. + */ + + if ((val & AUTO_RES_ERR_BIT) || + ((play_rate_code <= hap->drive_period_code_min_limit) || + (play_rate_code >= hap->drive_period_code_max_limit))) { + if (val & AUTO_RES_ERR_BIT) + pr_debug("Auto-resonance error %x\n", val); + else + pr_debug("play rate %x out of bounds [min: 0x%x, max: 0x%x]\n", + play_rate_code, + hap->drive_period_code_min_limit, + hap->drive_period_code_max_limit); + rc = qpnp_hap_auto_res_enable(hap, 0); + if (rc < 0) + pr_debug("Auto-resonance write failed\n"); + return; } + + qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), + lra_auto_res_lo); + + lra_auto_res_hi = lra_auto_res_hi >> 4; + qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), + lra_auto_res_hi); } static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) { struct qpnp_hap *hap = container_of(timer, struct qpnp_hap, auto_res_err_poll_timer); - u8 val; ktime_t currtime; - qpnp_hap_read_reg(hap, &val, QPNP_HAP_STATUS(hap->base)); - - if (val & AUTO_RES_ERR_BIT) { - schedule_work(&hap->auto_res_err_work); + if (!(hap->status_flags & AUTO_RESONANCE_ENABLED)) return HRTIMER_NORESTART; - } update_lra_frequency(hap); currtime = ktime_get(); @@ -1406,55 +1583,13 @@ static enum hrtimer_restart detect_auto_res_error(struct hrtimer *timer) return HRTIMER_RESTART; } -static void correct_auto_res_error(struct work_struct *auto_res_err_work) -{ - struct qpnp_hap *hap = container_of(auto_res_err_work, - struct qpnp_hap, auto_res_err_work); - - u8 lra_code_lo, lra_code_hi, disable_hap = 0x00; - static int lra_freq_index; - ktime_t currtime, remaining_time; - int temp, rem = 0, index = lra_freq_index % LRA_POS_FREQ_COUNT; - - if (hrtimer_active(&hap->hap_timer)) { - remaining_time = hrtimer_get_remaining(&hap->hap_timer); - rem = (int)ktime_to_us(remaining_time); - } - - qpnp_hap_play(hap, 0); - qpnp_hap_write_reg(hap, &disable_hap, - QPNP_HAP_EN_CTL_REG(hap->base)); - - lra_code_lo = lra_play_rate_code[index] & QPNP_HAP_RATE_CFG1_MASK; - qpnp_hap_write_reg(hap, &lra_code_lo, - QPNP_HAP_RATE_CFG1_REG(hap->base)); - - qpnp_hap_read_reg(hap, &lra_code_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); - - lra_code_hi &= QPNP_HAP_RATE_CFG2_MASK; - temp = lra_play_rate_code[index] >> QPNP_HAP_RATE_CFG2_SHFT; - lra_code_hi |= temp; - - qpnp_hap_write_reg(hap, &lra_code_hi, - QPNP_HAP_RATE_CFG2_REG(hap->base)); - - lra_freq_index++; - - if (rem > 0) { - currtime = ktime_get(); - hap->state = 1; - hrtimer_forward(&hap->hap_timer, currtime, remaining_time); - schedule_work(&hap->work); - } -} - /* set api for haptics */ static int qpnp_hap_set(struct qpnp_hap *hap, int on) { + u8 auto_res_mode_qwd; int rc = 0; - u8 val = 0; unsigned long timeout_ns = POLL_TIME_AUTO_RES_ERR_NS; + u32 back_emf_delay_us = hap->time_required_to_generate_back_emf_us; if (hap->play_mode == QPNP_HAP_PWM) { if (on) @@ -1464,8 +1599,28 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) } else if (hap->play_mode == QPNP_HAP_BUFFER || hap->play_mode == QPNP_HAP_DIRECT) { if (on) { - if (hap->correct_lra_drive_freq || - hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) + /* + * For auto resonance detection to work properly, + * sufficient back-emf has to be generated. In general, + * back-emf takes some time to build up. When the auto + * resonance mode is chosen as QWD, high-z will be + * applied for every LRA cycle and hence there won't be + * enough back-emf at the start-up. Hence, the motor + * needs to vibrate for few LRA cycles after the PLAY + * bit is asserted. So disable the auto resonance here + * and enable it after the sleep of + * 'time_required_to_generate_back_emf_us' is completed. + */ + if (hap->pmic_subtype == PM660_SUBTYPE) + auto_res_mode_qwd = (hap->auto_res_mode == + QPNP_HAP_PM660_AUTO_RES_QWD); + else + auto_res_mode_qwd = (hap->auto_res_mode == + QPNP_HAP_AUTO_RES_QWD); + + if ((hap->act_type == QPNP_HAP_LRA) && + (hap->correct_lra_drive_freq || + auto_res_mode_qwd)) qpnp_hap_auto_res_enable(hap, 0); rc = qpnp_hap_mod_enable(hap, on); @@ -1474,17 +1629,19 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) rc = qpnp_hap_play(hap, on); - if ((hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq) || - hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { - usleep_range(AUTO_RES_ENABLE_TIMEOUT, - (AUTO_RES_ENABLE_TIMEOUT + 1)); + if ((hap->act_type == QPNP_HAP_LRA) && + (hap->correct_lra_drive_freq || + auto_res_mode_qwd)) { + usleep_range(back_emf_delay_us, + (back_emf_delay_us + 1)); rc = qpnp_hap_auto_res_enable(hap, 1); if (rc < 0) return rc; } - if (hap->correct_lra_drive_freq) { + if (hap->act_type == QPNP_HAP_LRA && + hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) { /* * Start timer to poll Auto Resonance error bit */ @@ -1500,18 +1657,18 @@ static int qpnp_hap_set(struct qpnp_hap *hap, int on) if (rc < 0) return rc; - if (hap->correct_lra_drive_freq) { - rc = qpnp_hap_read_reg(hap, &val, - QPNP_HAP_STATUS(hap->base)); - if (!(val & AUTO_RES_ERR_BIT)) - update_lra_frequency(hap); + if (hap->act_type == QPNP_HAP_LRA && + hap->correct_lra_drive_freq && + (hap->status_flags & AUTO_RESONANCE_ENABLED) && + !hap->lra_hw_auto_resonance) { + update_lra_frequency(hap); } rc = qpnp_hap_mod_enable(hap, on); if (hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq) { + hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) { hrtimer_cancel(&hap->auto_res_err_poll_timer); - calculate_lra_code(hap); } } } @@ -1528,7 +1685,8 @@ static void qpnp_hap_td_enable(struct timed_output_dev *dev, int value) mutex_lock(&hap->lock); if (hap->act_type == QPNP_HAP_LRA && - hap->correct_lra_drive_freq) + hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); @@ -1563,7 +1721,7 @@ int qpnp_hap_play_byte(u8 data, bool on) } if (hap->play_mode != QPNP_HAP_PWM) { - dev_err(&hap->pdev->dev, "only PWM mode is supported\n"); + pr_err("only PWM mode is supported\n"); return -EINVAL; } @@ -1604,8 +1762,7 @@ int qpnp_hap_play_byte(u8 data, bool on) if (rc) return rc; - dev_dbg(&hap->pdev->dev, "data=0x%x duty_per=%d\n", data, - duty_percent); + pr_debug("data=0x%x duty_per=%d\n", data, duty_percent); rc = qpnp_hap_set(hap, true); @@ -1619,32 +1776,36 @@ static void qpnp_hap_worker(struct work_struct *work) struct qpnp_hap *hap = container_of(work, struct qpnp_hap, work); u8 val = 0x00; - int rc, reg_en = 0; + int rc; - if (hap->vcc_pon) { - reg_en = regulator_enable(hap->vcc_pon); - if (reg_en) - pr_err("%s: could not enable vcc_pon regulator\n", - __func__); + if (hap->vcc_pon && hap->state && !hap->vcc_pon_enabled) { + rc = regulator_enable(hap->vcc_pon); + if (rc < 0) + pr_err("could not enable vcc_pon regulator rc=%d\n", + rc); + else + hap->vcc_pon_enabled = true; } /* Disable haptics module if the duration of short circuit * exceeds the maximum limit (5 secs). */ if (hap->sc_duration == SC_MAX_DURATION) { - rc = qpnp_hap_write_reg(hap, &val, - QPNP_HAP_EN_CTL_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), + val); } else { if (hap->play_mode == QPNP_HAP_PWM) qpnp_hap_mod_enable(hap, hap->state); qpnp_hap_set(hap, hap->state); } - if (hap->vcc_pon && !reg_en) { + if (hap->vcc_pon && !hap->state && hap->vcc_pon_enabled) { rc = regulator_disable(hap->vcc_pon); if (rc) - pr_err("%s: could not disable vcc_pon regulator\n", - __func__); + pr_err("could not disable vcc_pon regulator rc=%d\n", + rc); + else + hap->vcc_pon_enabled = false; } } @@ -1706,51 +1867,25 @@ static SIMPLE_DEV_PM_OPS(qpnp_haptic_pm_ops, qpnp_haptic_suspend, NULL); /* Configuration api for haptics registers */ static int qpnp_hap_config(struct qpnp_hap *hap) { - u8 reg = 0, unlock_val, error_value; - int rc, i, temp; - uint error_code = 0; + u8 val = 0; + u32 temp; + int rc, i; - /* Configure the ACTUATOR TYPE register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_ACT_TYPE_MASK; - reg |= hap->act_type; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ACT_TYPE_REG(hap->base)); + /* + * This denotes the percentage error in rc clock multiplied by 10 + */ + u8 rc_clk_err_percent_x10; + + /* Configure the CFG1 register for actuator type */ + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG1_REG(hap->base), + QPNP_HAP_ACT_TYPE_MASK, hap->act_type); if (rc) return rc; /* Configure auto resonance parameters */ - if (hap->act_type == QPNP_HAP_LRA) { - if (hap->lra_res_cal_period < QPNP_HAP_RES_CAL_PERIOD_MIN) - hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MIN; - else if (hap->lra_res_cal_period > QPNP_HAP_RES_CAL_PERIOD_MAX) - hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; - - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_AUTO_RES_MODE_MASK; - reg |= (hap->auto_res_mode << QPNP_HAP_AUTO_RES_MODE_SHIFT); - reg &= QPNP_HAP_LRA_HIGH_Z_MASK; - reg |= (hap->lra_high_z << QPNP_HAP_LRA_HIGH_Z_SHIFT); - reg &= QPNP_HAP_LRA_RES_CAL_PER_MASK; - temp = fls(hap->lra_res_cal_period) - 1; - reg |= (temp - 2); - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); - if (rc) - return rc; - } else { - /* disable auto resonance for ERM */ - reg = 0x00; - - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_LRA_AUTO_RES_REG(hap->base)); - if (rc) - return rc; - } + rc = qpnp_hap_lra_auto_res_config(hap); + if (rc) + return rc; /* Configure the PLAY MODE register */ rc = qpnp_hap_play_mode_config(hap); @@ -1763,18 +1898,7 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure the ILIM register */ - if (hap->ilim_ma < QPNP_HAP_ILIM_MIN_MA) - hap->ilim_ma = QPNP_HAP_ILIM_MIN_MA; - else if (hap->ilim_ma > QPNP_HAP_ILIM_MAX_MA) - hap->ilim_ma = QPNP_HAP_ILIM_MAX_MA; - - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_ILIM_MASK; - temp = (hap->ilim_ma / QPNP_HAP_ILIM_MIN_MA) >> 1; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_ILIM_REG(hap->base)); + rc = qpnp_hap_ilim_config(hap); if (rc) return rc; @@ -1784,47 +1908,13 @@ static int qpnp_hap_config(struct qpnp_hap *hap) return rc; /* Configure the INTERNAL_PWM register */ - if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_253_KHZ) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_253_KHZ; - temp = 0; - } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_505_KHZ) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_505_KHZ; - temp = 1; - } else if (hap->int_pwm_freq_khz <= QPNP_HAP_INT_PWM_FREQ_739_KHZ) { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_739_KHZ; - temp = 2; - } else { - hap->int_pwm_freq_khz = QPNP_HAP_INT_PWM_FREQ_1076_KHZ; - temp = 3; - } - - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_INT_PWM_MASK; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_INT_PWM_REG(hap->base)); - if (rc) - return rc; - - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_INT_PWM_MASK; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_PWM_CAP_REG(hap->base)); + rc = qpnp_hap_int_pwm_config(hap); if (rc) return rc; /* Configure the WAVE SHAPE register */ - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_WAV_SHAPE_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_WAV_SHAPE_MASK; - reg |= hap->wave_shape; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_WAV_SHAPE_REG(hap->base)); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_CFG2_REG(hap->base), + QPNP_HAP_WAV_SHAPE_MASK, hap->wave_shape); if (rc) return rc; @@ -1838,86 +1928,108 @@ static int qpnp_hap_config(struct qpnp_hap *hap) else if (hap->wave_play_rate_us > QPNP_HAP_WAV_PLAY_RATE_US_MAX) hap->wave_play_rate_us = QPNP_HAP_WAV_PLAY_RATE_US_MAX; - temp = hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; + hap->init_drive_period_code = + hap->wave_play_rate_us / QPNP_HAP_RATE_CFG_STEP_US; /* - * The frequency of 19.2Mzhz RC clock is subject to variation. - * In PMI8950, TRIM_ERROR_RC19P2_CLK register in MISC module - * holds the frequency error in 19.2Mhz RC clock + * The frequency of 19.2Mzhz RC clock is subject to variation. Currently + * a few PMI modules have MISC_TRIM_ERROR_RC19P2_CLK register + * present in their MISC block. This register holds the frequency error + * in 19.2 MHz RC clock. */ if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq - && hap->misc_trim_error_rc19p2_clk_reg_present) { - unlock_val = MISC_SEC_UNLOCK; - rc = regmap_write(hap->regmap, MISC_SEC_ACCESS, unlock_val); - if (rc) - dev_err(&hap->pdev->dev, - "Unable to do SEC_ACCESS rc:%d\n", rc); - - regmap_read(hap->regmap, MISC_TRIM_ERROR_RC19P2_CLK, - &error_code); + && hap->misc_clk_trim_error_reg) { + pr_debug("TRIM register = 0x%x\n", hap->clk_trim_error_code); - error_value = (error_code & 0x0F) * 7; + /* + * Extract the 4 LSBs and multiply by 7 to get + * the %error in RC clock multiplied by 10 + */ + rc_clk_err_percent_x10 = (hap->clk_trim_error_code & 0x0F) * 7; - if (error_code & 0x80) - temp = (temp * (1000 - error_value)) / 1000; + /* + * If the TRIM register holds value less than 0x80, + * then there is a positive error in the RC clock. + * If the TRIM register holds value greater than or equal to + * 0x80, then there is a negative error in the RC clock. Bit 7 + * is the sign bit for error code. + * + * The adjusted play rate code is calculated as follows: + * LRA drive period code (RATE_CFG) = + * 200KHz * 1 / LRA drive frequency * ( 1 + %error/ 100) + * + * This can be rewritten as: + * LRA drive period code (RATE_CFG) = + * 200KHz * 1/LRA drive frequency *( 1 + %error * 10/1000) + * + * Since 200KHz * 1/LRA drive frequency is already calculated + * above we only do rest of the scaling here. + */ + if (hap->clk_trim_error_code & BIT(7)) + LRA_DRIVE_PERIOD_NEG_ERR(hap, rc_clk_err_percent_x10); else - temp = (temp * (1000 + error_value)) / 1000; + LRA_DRIVE_PERIOD_POS_ERR(hap, rc_clk_err_percent_x10); } - reg = temp & QPNP_HAP_RATE_CFG1_MASK; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_RATE_CFG1_REG(hap->base)); + pr_debug("Play rate code 0x%x\n", hap->init_drive_period_code); + + val = hap->init_drive_period_code & QPNP_HAP_RATE_CFG1_MASK; + rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG1_REG(hap->base), val); if (rc) return rc; - rc = qpnp_hap_read_reg(hap, ®, - QPNP_HAP_RATE_CFG2_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_RATE_CFG2_MASK; - temp = temp >> QPNP_HAP_RATE_CFG2_SHFT; - reg |= temp; - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_RATE_CFG2_REG(hap->base)); + val = (hap->init_drive_period_code & 0xF00) >> QPNP_HAP_RATE_CFG2_SHFT; + rc = qpnp_hap_write_reg(hap, QPNP_HAP_RATE_CFG2_REG(hap->base), val); if (rc) return rc; - if ((hap->act_type == QPNP_HAP_LRA) && hap->correct_lra_drive_freq) + if (hap->act_type == QPNP_HAP_LRA && + hap->perform_lra_auto_resonance_search) calculate_lra_code(hap); + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { + hap->drive_period_code_max_limit = + (hap->init_drive_period_code * (100 + + hap->drive_period_code_max_limit_percent_variation)) + / 100; + hap->drive_period_code_min_limit = + (hap->init_drive_period_code * (100 - + hap->drive_period_code_min_limit_percent_variation)) + / 100; + pr_debug("Drive period code max limit %x min limit %x\n", + hap->drive_period_code_max_limit, + hap->drive_period_code_min_limit); + } + /* Configure BRAKE register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); - if (rc < 0) - return rc; - reg &= QPNP_HAP_BRAKE_MASK; - reg |= hap->en_brake; - rc = qpnp_hap_write_reg(hap, ®, QPNP_HAP_EN_CTL2_REG(hap->base)); + rc = qpnp_hap_masked_write_reg(hap, QPNP_HAP_EN_CTL2_REG(hap->base), + QPNP_HAP_BRAKE_MASK, (u8)hap->en_brake); if (rc) return rc; if (hap->en_brake && hap->sup_brake_pat) { - for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, reg = 0; i >= 0; i--) { + for (i = QPNP_HAP_BRAKE_PAT_LEN - 1, val = 0; i >= 0; i--) { hap->brake_pat[i] &= QPNP_HAP_BRAKE_PAT_MASK; temp = i << 1; - reg |= hap->brake_pat[i] << temp; + val |= hap->brake_pat[i] << temp; } - rc = qpnp_hap_write_reg(hap, ®, - QPNP_HAP_BRAKE_REG(hap->base)); + rc = qpnp_hap_write_reg(hap, QPNP_HAP_BRAKE_REG(hap->base), + val); if (rc) return rc; } /* Cache enable control register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_EN_CTL_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_EN_CTL_REG(hap->base), &val); if (rc < 0) return rc; - hap->reg_en_ctl = reg; + hap->reg_en_ctl = val; /* Cache play register */ - rc = qpnp_hap_read_reg(hap, ®, QPNP_HAP_PLAY_REG(hap->base)); + rc = qpnp_hap_read_reg(hap, QPNP_HAP_PLAY_REG(hap->base), &val); if (rc < 0) return rc; - hap->reg_play = reg; + hap->reg_play = val; if (hap->play_mode == QPNP_HAP_BUFFER) rc = qpnp_hap_buffer_config(hap); @@ -1929,15 +2041,29 @@ static int qpnp_hap_config(struct qpnp_hap *hap) if (rc) return rc; + /* setup play irq */ + if (hap->play_irq >= 0) { + rc = devm_request_threaded_irq(&hap->pdev->dev, hap->play_irq, + NULL, qpnp_hap_play_irq, IRQF_ONESHOT, "qpnp_hap_play", + hap); + if (rc < 0) { + pr_err("Unable to request play(%d) IRQ(err:%d)\n", + hap->play_irq, rc); + return rc; + } + + /* use play_irq only for buffer mode */ + if (hap->play_mode != QPNP_HAP_BUFFER) + disable_irq(hap->play_irq); + } + /* setup short circuit irq */ - if (hap->use_sc_irq) { + if (hap->sc_irq >= 0) { rc = devm_request_threaded_irq(&hap->pdev->dev, hap->sc_irq, - NULL, qpnp_hap_sc_irq, - QPNP_IRQ_FLAGS, - "qpnp_sc_irq", hap); + NULL, qpnp_hap_sc_irq, IRQF_ONESHOT, "qpnp_hap_sc", + hap); if (rc < 0) { - dev_err(&hap->pdev->dev, - "Unable to request sc(%d) IRQ(err:%d)\n", + pr_err("Unable to request sc(%d) IRQ(err:%d)\n", hap->sc_irq, rc); return rc; } @@ -1952,18 +2078,46 @@ static int qpnp_hap_config(struct qpnp_hap *hap) static int qpnp_hap_parse_dt(struct qpnp_hap *hap) { struct platform_device *pdev = hap->pdev; + struct device_node *misc_node; struct property *prop; const char *temp_str; u32 temp; int rc; + if (of_find_property(pdev->dev.of_node, "qcom,pmic-misc", NULL)) { + misc_node = of_parse_phandle(pdev->dev.of_node, + "qcom,pmic-misc", 0); + if (!misc_node) + return -EINVAL; + + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,misc-clk-trim-error-reg", &temp); + if (rc < 0) { + pr_err("Missing misc-clk-trim-error-reg\n"); + return rc; + } + + if (!temp || temp > 0xFF) { + pr_err("Invalid misc-clk-trim-error-reg\n"); + return -EINVAL; + } + + hap->misc_clk_trim_error_reg = temp; + rc = qpnp_misc_read_reg(misc_node, hap->misc_clk_trim_error_reg, + &hap->clk_trim_error_code); + if (rc < 0) { + pr_err("Couldn't get clk_trim_error_code, rc=%d\n", rc); + return -EPROBE_DEFER; + } + } + hap->timeout_ms = QPNP_HAP_TIMEOUT_MS_MAX; rc = of_property_read_u32(pdev->dev.of_node, "qcom,timeout-ms", &temp); if (!rc) { hap->timeout_ms = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read timeout\n"); + pr_err("Unable to read timeout\n"); return rc; } @@ -1976,31 +2130,47 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "lra") == 0) hap->act_type = QPNP_HAP_LRA; else { - dev_err(&pdev->dev, "Invalid actuator type\n"); + pr_err("Invalid actuator type\n"); return -EINVAL; } } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read actuator type\n"); + pr_err("Unable to read actuator type\n"); return rc; } if (hap->act_type == QPNP_HAP_LRA) { - hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; rc = of_property_read_string(pdev->dev.of_node, "qcom,lra-auto-res-mode", &temp_str); if (!rc) { - if (strcmp(temp_str, "none") == 0) - hap->auto_res_mode = QPNP_HAP_AUTO_RES_NONE; - else if (strcmp(temp_str, "zxd") == 0) - hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD; - else if (strcmp(temp_str, "qwd") == 0) - hap->auto_res_mode = QPNP_HAP_AUTO_RES_QWD; - else if (strcmp(temp_str, "max-qwd") == 0) - hap->auto_res_mode = QPNP_HAP_AUTO_RES_MAX_QWD; - else + if (hap->pmic_subtype == PM660_SUBTYPE) { + hap->auto_res_mode = + QPNP_HAP_PM660_AUTO_RES_QWD; + if (strcmp(temp_str, "zxd") == 0) + hap->auto_res_mode = + QPNP_HAP_PM660_AUTO_RES_ZXD; + else if (strcmp(temp_str, "qwd") == 0) + hap->auto_res_mode = + QPNP_HAP_PM660_AUTO_RES_QWD; + } else { hap->auto_res_mode = QPNP_HAP_AUTO_RES_ZXD_EOP; + if (strcmp(temp_str, "none") == 0) + hap->auto_res_mode = + QPNP_HAP_AUTO_RES_NONE; + else if (strcmp(temp_str, "zxd") == 0) + hap->auto_res_mode = + QPNP_HAP_AUTO_RES_ZXD; + else if (strcmp(temp_str, "qwd") == 0) + hap->auto_res_mode = + QPNP_HAP_AUTO_RES_QWD; + else if (strcmp(temp_str, "max-qwd") == 0) + hap->auto_res_mode = + QPNP_HAP_AUTO_RES_MAX_QWD; + else + hap->auto_res_mode = + QPNP_HAP_AUTO_RES_ZXD_EOP; + } } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read auto res mode\n"); + pr_err("Unable to read auto res mode\n"); return rc; } @@ -2010,6 +2180,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { if (strcmp(temp_str, "none") == 0) hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_NONE; + if (hap->pmic_subtype == PM660_SUBTYPE) { + if (strcmp(temp_str, "opt0") == 0) + hap->lra_high_z = + QPNP_HAP_LRA_HIGH_Z_NONE; + } else if (strcmp(temp_str, "opt1") == 0) hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT1; else if (strcmp(temp_str, "opt2") == 0) @@ -2017,27 +2192,67 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else hap->lra_high_z = QPNP_HAP_LRA_HIGH_Z_OPT3; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read LRA high-z\n"); + pr_err("Unable to read LRA high-z\n"); return rc; } + hap->lra_qwd_drive_duration = -EINVAL; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,lra-qwd-drive-duration", + &hap->lra_qwd_drive_duration); + + hap->calibrate_at_eop = -EINVAL; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,lra-calibrate-at-eop", &hap->calibrate_at_eop); + hap->lra_res_cal_period = QPNP_HAP_RES_CAL_PERIOD_MAX; rc = of_property_read_u32(pdev->dev.of_node, "qcom,lra-res-cal-period", &temp); if (!rc) { hap->lra_res_cal_period = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read cal period\n"); + pr_err("Unable to read cal period\n"); return rc; } + hap->lra_hw_auto_resonance = + of_property_read_bool(pdev->dev.of_node, + "qcom,lra-hw-auto-resonance"); + + hap->perform_lra_auto_resonance_search = + of_property_read_bool(pdev->dev.of_node, + "qcom,perform-lra-auto-resonance-search"); + hap->correct_lra_drive_freq = of_property_read_bool(pdev->dev.of_node, "qcom,correct-lra-drive-freq"); - hap->misc_trim_error_rc19p2_clk_reg_present = - of_property_read_bool(pdev->dev.of_node, - "qcom,misc-trim-error-rc19p2-clk-reg-present"); + hap->drive_period_code_max_limit_percent_variation = 25; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,drive-period-code-max-limit-percent-variation", &temp); + if (!rc) + hap->drive_period_code_max_limit_percent_variation = + (u8) temp; + + hap->drive_period_code_min_limit_percent_variation = 25; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,drive-period-code-min-limit-percent-variation", &temp); + if (!rc) + hap->drive_period_code_min_limit_percent_variation = + (u8) temp; + + if (hap->auto_res_mode == QPNP_HAP_AUTO_RES_QWD) { + hap->time_required_to_generate_back_emf_us = + QPNP_HAP_TIME_REQ_FOR_BACK_EMF_GEN; + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,time-required-to-generate-back-emf-us", + &temp); + if (!rc) + hap->time_required_to_generate_back_emf_us = + temp; + } else { + hap->time_required_to_generate_back_emf_us = 0; + } } rc = of_property_read_string(pdev->dev.of_node, @@ -2052,11 +2267,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "audio") == 0) hap->play_mode = QPNP_HAP_AUDIO; else { - dev_err(&pdev->dev, "Invalid play mode\n"); + pr_err("Invalid play mode\n"); return -EINVAL; } } else { - dev_err(&pdev->dev, "Unable to read play mode\n"); + pr_err("Unable to read play mode\n"); return rc; } @@ -2065,7 +2280,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->vmax_mv = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read vmax\n"); + pr_err("Unable to read vmax\n"); return rc; } @@ -2074,7 +2289,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->ilim_ma = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read ILim\n"); + pr_err("Unable to read ILim\n"); return rc; } @@ -2084,7 +2299,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->sc_deb_cycles = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read sc debounce\n"); + pr_err("Unable to read sc debounce\n"); return rc; } @@ -2094,7 +2309,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->int_pwm_freq_khz = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read int pwm freq\n"); + pr_err("Unable to read int pwm freq\n"); return rc; } @@ -2107,11 +2322,11 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) else if (strcmp(temp_str, "square") == 0) hap->wave_shape = QPNP_HAP_WAV_SQUARE; else { - dev_err(&pdev->dev, "Unsupported wav shape\n"); + pr_err("Unsupported wav shape\n"); return -EINVAL; } } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read wav shape\n"); + pr_err("Unable to read wav shape\n"); return rc; } @@ -2121,7 +2336,7 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) if (!rc) { hap->wave_play_rate_us = temp; } else if (rc != -EINVAL) { - dev_err(&pdev->dev, "Unable to read play rate\n"); + pr_err("Unable to read play rate\n"); return rc; } @@ -2140,9 +2355,9 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) prop = of_find_property(pdev->dev.of_node, "qcom,brake-pattern", &temp); if (!prop) { - dev_info(&pdev->dev, "brake pattern not found"); + pr_info("brake pattern not found"); } else if (temp != QPNP_HAP_BRAKE_PAT_LEN) { - dev_err(&pdev->dev, "Invalid len of brake pattern\n"); + pr_err("Invalid len of brake pattern\n"); return -EINVAL; } else { hap->sup_brake_pat = true; @@ -2151,14 +2366,14 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) } } - hap->use_sc_irq = of_property_read_bool(pdev->dev.of_node, - "qcom,use-sc-irq"); - if (hap->use_sc_irq) { - hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq"); - if (hap->sc_irq < 0) { - dev_err(&pdev->dev, "Unable to get sc irq\n"); - return hap->sc_irq; - } + hap->play_irq = platform_get_irq_byname(hap->pdev, "play-irq"); + if (hap->play_irq < 0) + pr_warn("Unable to get play irq\n"); + + hap->sc_irq = platform_get_irq_byname(hap->pdev, "sc-irq"); + if (hap->sc_irq < 0) { + pr_err("Unable to get sc irq\n"); + return hap->sc_irq; } if (of_find_property(pdev->dev.of_node, "vcc_pon-supply", NULL)) @@ -2167,6 +2382,34 @@ static int qpnp_hap_parse_dt(struct qpnp_hap *hap) return 0; } +static int qpnp_hap_get_pmic_revid(struct qpnp_hap *hap) +{ + struct pmic_revid_data *pmic_rev_id; + struct device_node *revid_dev_node; + + revid_dev_node = of_parse_phandle(hap->pdev->dev.of_node, + "qcom,pmic-revid", 0); + if (!revid_dev_node) { + pr_err("Missing qcom,pmic-revid property - driver failed\n"); + return -EINVAL; + } + pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR_OR_NULL(pmic_rev_id)) { + pr_err("Unable to get pmic_revid rc=%ld\n", + PTR_ERR(pmic_rev_id)); + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + hap->pmic_subtype = pmic_rev_id->pmic_subtype; + + return 0; +} + static int qpnp_haptic_probe(struct platform_device *pdev) { struct qpnp_hap *hap; @@ -2179,7 +2422,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) return -ENOMEM; hap->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!hap->regmap) { - dev_err(&pdev->dev, "Couldn't get parent's regmap\n"); + pr_err("Couldn't get parent's regmap\n"); return -EINVAL; } @@ -2187,8 +2430,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = of_property_read_u32(pdev->dev.of_node, "reg", &base); if (rc < 0) { - dev_err(&pdev->dev, - "Couldn't find reg in node = %s rc = %d\n", + pr_err("Couldn't find reg in node = %s rc = %d\n", pdev->dev.of_node->full_name, rc); return rc; } @@ -2196,15 +2438,22 @@ static int qpnp_haptic_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, hap); + rc = qpnp_hap_get_pmic_revid(hap); + if (rc) { + pr_err("Unable to check PMIC version rc=%d\n", rc); + return rc; + } + rc = qpnp_hap_parse_dt(hap); if (rc) { - dev_err(&pdev->dev, "DT parsing failed\n"); + pr_err("DT parsing failed\n"); return rc; } + spin_lock_init(&hap->bus_lock); rc = qpnp_hap_config(hap); if (rc) { - dev_err(&pdev->dev, "hap config failed\n"); + pr_err("hap config failed\n"); return rc; } @@ -2224,8 +2473,8 @@ static int qpnp_haptic_probe(struct platform_device *pdev) hap->timed_dev.get_time = qpnp_hap_get_time; hap->timed_dev.enable = qpnp_hap_td_enable; - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) { - INIT_WORK(&hap->auto_res_err_work, correct_auto_res_error); + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) { hrtimer_init(&hap->auto_res_err_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); hap->auto_res_err_poll_timer.function = detect_auto_res_error; @@ -2233,7 +2482,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = timed_output_dev_register(&hap->timed_dev); if (rc < 0) { - dev_err(&pdev->dev, "timed_output registration failed\n"); + pr_err("timed_output registration failed\n"); goto timed_output_fail; } @@ -2241,7 +2490,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) rc = sysfs_create_file(&hap->timed_dev.dev->kobj, &qpnp_hap_attrs[i].attr); if (rc < 0) { - dev_err(&pdev->dev, "sysfs creation failed\n"); + pr_err("sysfs creation failed\n"); goto sysfs_fail; } } @@ -2250,8 +2499,7 @@ static int qpnp_haptic_probe(struct platform_device *pdev) vcc_pon = regulator_get(&pdev->dev, "vcc_pon"); if (IS_ERR(vcc_pon)) { rc = PTR_ERR(vcc_pon); - dev_err(&pdev->dev, - "regulator get failed vcc_pon rc=%d\n", rc); + pr_err("regulator get failed vcc_pon rc=%d\n", rc); goto sysfs_fail; } hap->vcc_pon = vcc_pon; @@ -2268,7 +2516,8 @@ sysfs_fail: timed_output_dev_unregister(&hap->timed_dev); timed_output_fail: cancel_work_sync(&hap->work); - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); mutex_destroy(&hap->lock); @@ -2287,7 +2536,8 @@ static int qpnp_haptic_remove(struct platform_device *pdev) &qpnp_hap_attrs[i].attr); cancel_work_sync(&hap->work); - if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq) + if (hap->act_type == QPNP_HAP_LRA && hap->correct_lra_drive_freq && + !hap->lra_hw_auto_resonance) hrtimer_cancel(&hap->auto_res_err_poll_timer); hrtimer_cancel(&hap->hap_timer); timed_output_dev_unregister(&hap->timed_dev); diff --git a/drivers/soc/qcom/rpm-smd.c b/drivers/soc/qcom/rpm-smd.c index 20f406b9a2f7..f2784dedbc7a 100644 --- a/drivers/soc/qcom/rpm-smd.c +++ b/drivers/soc/qcom/rpm-smd.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -716,51 +716,6 @@ int msm_rpm_smd_buffer_request(struct msm_rpm_request *cdata, return 0; } -static void msm_rpm_print_sleep_buffer(struct slp_buf *s) -{ - char buf[DEBUG_PRINT_BUFFER_SIZE] = {0}; - int pos; - int buflen = DEBUG_PRINT_BUFFER_SIZE; - char ch[5] = {0}; - struct kvp *e; - uint32_t type; - unsigned int id; - - if (!s) - return; - - if (!s->valid) - return; - - type = get_rsc_type(s->buf); - id = get_rsc_id(s->buf); - - memcpy(ch, &type, sizeof(u32)); - - pos = scnprintf(buf, buflen, - "Sleep request type = 0x%08x(%s)", - type, ch); - pos += scnprintf(buf + pos, buflen - pos, " id = 0%x", - id); - for_each_kvp(s->buf, e) { - uint32_t i; - char *data = get_data(e); - - memcpy(ch, &e->k, sizeof(u32)); - - pos += scnprintf(buf + pos, buflen - pos, - "\n\t\tkey = 0x%08x(%s)", - e->k, ch); - pos += scnprintf(buf + pos, buflen - pos, - " sz= %d data =", e->s); - - for (i = 0; i < e->s; i++) - pos += scnprintf(buf + pos, buflen - pos, - " 0x%02X", data[i]); - } - pos += scnprintf(buf + pos, buflen - pos, "\n"); - printk(buf); -} static struct msm_rpm_driver_data msm_rpm_data = { .smd_open = COMPLETION_INITIALIZER(msm_rpm_data.smd_open), @@ -821,9 +776,6 @@ static int msm_rpm_flush_requests(bool print) if (!s->valid) continue; - if (print) - msm_rpm_print_sleep_buffer(s); - set_msg_id(s->buf, msm_rpm_get_next_msg_id()); if (!glink_enabled) diff --git a/drivers/soc/qcom/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c index 90c7585d480c..4307937d9f6d 100644 --- a/drivers/soc/qcom/secure_buffer.c +++ b/drivers/soc/qcom/secure_buffer.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 Google, Inc - * Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -379,6 +379,7 @@ int hyp_assign_phys(phys_addr_t addr, u64 size, u32 *source_vm_list, sg_free_table(&table); return ret; } +EXPORT_SYMBOL(hyp_assign_phys); const char *msm_secure_vmid_to_string(int secure_vmid) { diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c index 8581ed587ead..0625f75de373 100644 --- a/drivers/soc/qcom/service-locator.c +++ b/drivers/soc/qcom/service-locator.c @@ -266,10 +266,11 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd) if (!domains_read) { db_rev_count = pd->db_rev_count = resp->db_rev_count; pd->total_domains = resp->total_domains; - if (!pd->total_domains && resp->domain_list_len) { - pr_err("total domains not set\n"); - pd->total_domains = resp->domain_list_len; + if (!resp->total_domains) { + pr_err("No matching domains found\n"); + goto out; } + pd->domain_list = kmalloc( sizeof(struct servreg_loc_entry_v01) * resp->total_domains, GFP_KERNEL); @@ -286,6 +287,10 @@ static int service_locator_send_msg(struct pd_qmi_client_data *pd) rc = -EAGAIN; goto out; } + if (resp->domain_list_len > resp->total_domains) { + /* Always read total_domains from the response msg */ + resp->domain_list_len = resp->total_domains; + } /* Copy the response*/ store_get_domain_list_response(pd, resp, domains_read); domains_read += resp->domain_list_len; @@ -372,6 +377,7 @@ int get_service_location(char *client_name, char *service_name, if (!pqw) { rc = -ENOMEM; pr_err("Allocation failed\n"); + kfree(pqcd); goto err; } pqw->notifier = locator_nb; diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c index 85ff81ff475c..fa916ac5ade4 100644 --- a/drivers/soc/qcom/service-notifier.c +++ b/drivers/soc/qcom/service-notifier.c @@ -99,10 +99,12 @@ struct ind_req_resp { */ struct qmi_client_info { int instance_id; + enum pd_subsys_state subsys_state; struct work_struct svc_arrive; struct work_struct svc_exit; struct work_struct svc_rcv_msg; struct work_struct ind_ack; + struct work_struct qmi_handle_free; struct workqueue_struct *svc_event_wq; struct qmi_handle *clnt_handle; struct notifier_block notifier; @@ -122,6 +124,18 @@ static void root_service_clnt_recv_msg(struct work_struct *work); static void root_service_service_arrive(struct work_struct *work); static void root_service_exit_work(struct work_struct *work); +static void free_qmi_handle(struct work_struct *work) +{ + struct qmi_client_info *data = container_of(work, + struct qmi_client_info, qmi_handle_free); + + mutex_lock(&qmi_client_release_lock); + data->service_connected = false; + qmi_handle_destroy(data->clnt_handle); + data->clnt_handle = NULL; + mutex_unlock(&qmi_client_release_lock); +} + static struct service_notif_info *_find_service_info(const char *service_path) { struct service_notif_info *service_notif; @@ -425,18 +439,14 @@ static void root_service_service_exit(struct qmi_client_info *data, * Destroy client handle and try connecting when * service comes up again. */ - mutex_lock(&qmi_client_release_lock); - data->service_connected = false; - qmi_handle_destroy(data->clnt_handle); - data->clnt_handle = NULL; - mutex_unlock(&qmi_client_release_lock); + queue_work(data->svc_event_wq, &data->qmi_handle_free); } static void root_service_exit_work(struct work_struct *work) { struct qmi_client_info *data = container_of(work, struct qmi_client_info, svc_exit); - root_service_service_exit(data, ROOT_PD_DOWN); + root_service_service_exit(data, data->subsys_state); } static int service_event_notify(struct notifier_block *this, @@ -453,6 +463,7 @@ static int service_event_notify(struct notifier_block *this, break; case QMI_SERVER_EXIT: pr_debug("Root PD service DOWN\n"); + data->subsys_state = ROOT_PD_DOWN; queue_work(data->svc_event_wq, &data->svc_exit); break; default: @@ -468,7 +479,6 @@ static int ssr_event_notify(struct notifier_block *this, struct qmi_client_info *info = container_of(this, struct qmi_client_info, ssr_notifier); struct notif_data *notif = data; - enum pd_subsys_state state; switch (code) { case SUBSYS_BEFORE_SHUTDOWN: @@ -476,16 +486,16 @@ static int ssr_event_notify(struct notifier_block *this, notif->crashed); switch (notif->crashed) { case CRASH_STATUS_ERR_FATAL: - state = ROOT_PD_ERR_FATAL; + info->subsys_state = ROOT_PD_ERR_FATAL; break; case CRASH_STATUS_WDOG_BITE: - state = ROOT_PD_WDOG_BITE; + info->subsys_state = ROOT_PD_WDOG_BITE; break; default: - state = ROOT_PD_SHUTDOWN; + info->subsys_state = ROOT_PD_SHUTDOWN; break; } - root_service_service_exit(info, state); + root_service_service_exit(info, info->subsys_state); break; default: break; @@ -560,6 +570,7 @@ static void *add_service_notif(const char *service_path, int instance_id, INIT_WORK(&qmi_data->svc_exit, root_service_exit_work); INIT_WORK(&qmi_data->svc_rcv_msg, root_service_clnt_recv_msg); INIT_WORK(&qmi_data->ind_ack, send_ind_ack); + INIT_WORK(&qmi_data->qmi_handle_free, free_qmi_handle); *curr_state = service_notif->curr_state = SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01; @@ -635,7 +646,13 @@ static int send_pd_restart_req(const char *service_path, return rc; } - /* Check the response */ + /* Check response if PDR is disabled */ + if (QMI_RESP_BIT_SHIFT(resp.resp.result) == QMI_ERR_DISABLED_V01) { + pr_err("PD restart is disabled 0x%x\n", + QMI_RESP_BIT_SHIFT(resp.resp.error)); + return -EOPNOTSUPP; + } + /* Check the response for other error case*/ if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01) { pr_err("QMI request for PD restart failed 0x%x\n", QMI_RESP_BIT_SHIFT(resp.resp.error)); diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c index 07610877f140..0c44d76bc7c7 100644 --- a/drivers/soc/qcom/spcom.c +++ b/drivers/soc/qcom/spcom.c @@ -1744,7 +1744,9 @@ static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch, } } - pr_err("fd [%d] ion buf not found.\n", fd); + pr_err("no free entry to store ion handle of fd [%d].\n", fd); + /* decrement back the ref count */ + ion_free(spcom_dev->ion_client, ion_handle); return -EFAULT; } @@ -2241,7 +2243,7 @@ static ssize_t spcom_device_write(struct file *filp, } /** - * spcom_device_read() - handle channel file write() from user space. + * spcom_device_read() - handle channel file read() from user space. * * @filp: file pointer * @@ -2267,6 +2269,16 @@ static ssize_t spcom_device_read(struct file *filp, char __user *user_buff, ch = filp->private_data; + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return -EINVAL; + } + + if (!spcom_is_channel_open(ch)) { + pr_err("ch is not open, file [%s].\n", name); + return -EINVAL; + } + buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; @@ -2354,6 +2366,10 @@ static unsigned int spcom_device_poll(struct file *filp, done = (spcom_dev->link_state == GLINK_LINK_STATE_UP); break; case SPCOM_POLL_CH_CONNECT: + if (ch == NULL) { + pr_err("invalid ch pointer, file [%s].\n", name); + return -EINVAL; + } pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name); if (wait) { reinit_completion(&ch->connect); diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index b04b05a0904e..5548a31e1a39 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -116,7 +116,7 @@ static const struct spm_reg_data spm_reg_8064_cpu = { static DEFINE_PER_CPU(struct spm_driver_data *, cpu_spm_drv); -typedef int (*idle_fn)(int); +typedef int (*idle_fn)(void); static DEFINE_PER_CPU(idle_fn*, qcom_idle_ops); static inline void spm_register_write(struct spm_driver_data *drv, @@ -179,10 +179,10 @@ static int qcom_pm_collapse(unsigned long int unused) return -1; } -static int qcom_cpu_spc(int cpu) +static int qcom_cpu_spc(void) { int ret; - struct spm_driver_data *drv = per_cpu(cpu_spm_drv, cpu); + struct spm_driver_data *drv = __this_cpu_read(cpu_spm_drv); spm_set_low_power_mode(drv, PM_SLEEP_MODE_SPC); ret = cpu_suspend(0, qcom_pm_collapse); @@ -197,9 +197,9 @@ static int qcom_cpu_spc(int cpu) return ret; } -static int qcom_idle_enter(int cpu, unsigned long index) +static int qcom_idle_enter(unsigned long index) { - return per_cpu(qcom_idle_ops, cpu)[index](cpu); + return __this_cpu_read(qcom_idle_ops)[index](); } static const struct of_device_id qcom_idle_state_match[] __initconst = { @@ -288,7 +288,7 @@ static struct spm_driver_data *spm_get_drv(struct platform_device *pdev, struct spm_driver_data *drv = NULL; struct device_node *cpu_node, *saw_node; int cpu; - bool found; + bool found = 0; for_each_possible_cpu(cpu) { cpu_node = of_cpu_device_node_get(cpu); diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c index c6531de48f65..991bce363740 100644 --- a/drivers/soc/qcom/subsys-pil-tz.c +++ b/drivers/soc/qcom/subsys-pil-tz.c @@ -1034,6 +1034,7 @@ static int pil_tz_driver_probe(struct platform_device *pdev) d->desc.ops = &pil_ops_trusted; d->desc.proxy_timeout = PROXY_TIMEOUT_MS; + d->desc.clear_fw_region = true; rc = of_property_read_u32(pdev->dev.of_node, "qcom,proxy-timeout-ms", &proxy_timeout); diff --git a/drivers/soc/qcom/sysmon-qmi.c b/drivers/soc/qcom/sysmon-qmi.c index 7ef69b527ef8..1063b96d8d83 100644 --- a/drivers/soc/qcom/sysmon-qmi.c +++ b/drivers/soc/qcom/sysmon-qmi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -153,10 +153,12 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work) struct sysmon_qmi_data *data = container_of(work, struct sysmon_qmi_data, svc_arrive); + mutex_lock(&sysmon_lock); /* Create a Local client port for QMI communication */ data->clnt_handle = qmi_handle_create(sysmon_clnt_notify, work); if (!data->clnt_handle) { pr_err("QMI client handle alloc failed for %s\n", data->name); + mutex_unlock(&sysmon_lock); return; } @@ -167,6 +169,7 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work) data->name); qmi_handle_destroy(data->clnt_handle); data->clnt_handle = NULL; + mutex_unlock(&sysmon_lock); return; } pr_info("Connection established between QMI handle and %s's SSCTL service\n" @@ -177,6 +180,7 @@ static void sysmon_clnt_svc_arrive(struct work_struct *work) if (rc < 0) pr_warn("%s: Could not register the indication callback\n", data->name); + mutex_unlock(&sysmon_lock); } static void sysmon_clnt_svc_exit(struct work_struct *work) @@ -184,8 +188,10 @@ static void sysmon_clnt_svc_exit(struct work_struct *work) struct sysmon_qmi_data *data = container_of(work, struct sysmon_qmi_data, svc_exit); + mutex_lock(&sysmon_lock); qmi_handle_destroy(data->clnt_handle); data->clnt_handle = NULL; + mutex_unlock(&sysmon_lock); } static void sysmon_clnt_recv_msg(struct work_struct *work) diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c index 1ceded4db79f..f601e6646852 100644 --- a/drivers/soc/qcom/wcd-dsp-glink.c +++ b/drivers/soc/qcom/wcd-dsp-glink.c @@ -531,6 +531,13 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, u8 *payload; u32 ch_size, ch_cfg_size; + mutex_lock(&wpriv->glink_mutex); + if (wpriv->ch) { + dev_err(wpriv->dev, "%s: glink ch memory is already allocated\n", + __func__); + ret = -EINVAL; + goto done; + } payload = (u8 *)pkt->payload; no_of_channels = pkt->no_of_channels; @@ -611,6 +618,7 @@ err_ch_mem: wpriv->no_of_channels = 0; done: + mutex_unlock(&wpriv->glink_mutex); return ret; } |
