OSDN Git Service

input: qpnp-power-on: Add debounce for KPDPWR_N rising edge
authorAnirudh Ghayal <aghayal@codeaurora.org>
Wed, 19 Apr 2017 09:53:06 +0000 (15:23 +0530)
committerAnirudh Ghayal <aghayal@codeaurora.org>
Thu, 27 Apr 2017 01:25:50 +0000 (06:55 +0530)
On certain PMICs, an unexpected assertion on KPDPWR_DBC may
be seen during falling edge of KPDPWR_N when it is closer
to the rising edge of SLEEP_CLK. This triggers spurious
KPDPWR interrupts. Handle this by adding a debounce in SW
when the first KPDPWR_N falling edge is seen.

The debounce logic is enabled by the DT property
'qcom,kpdpwr-sw-debounce'.

CRs-Fixed: 2032520
Change-Id: I7655c13bda47fa6e2983650d7bec21e52aa91c2f
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
Documentation/devicetree/bindings/input/qpnp-power-on.txt
drivers/input/misc/qpnp-power-on.c

index a596aa1..c2550e6 100644 (file)
@@ -82,6 +82,8 @@ Optional properties:
 - qcom,shutdown-poweroff-type  Same description as qcom,warm-reset-poweroff-
                                type but this applies for the system shutdown
                                case.
+- qcom,kpdpwr-sw-debounce      Boolean property to enable the debounce logic
+                               on the KPDPWR_N rising edge.
 
 
 All the below properties are in the sub-node section (properties of the child
index add11d4..339f94c 100644 (file)
@@ -207,7 +207,7 @@ struct qpnp_pon {
        int                     pon_power_off_reason;
        int                     num_pon_reg;
        int                     num_pon_config;
-       u32                     dbc;
+       u32                     dbc_time_us;
        u32                     uvlo;
        int                     warm_reset_poff_type;
        int                     hard_reset_poff_type;
@@ -219,6 +219,8 @@ struct qpnp_pon {
        u8                      warm_reset_reason2;
        bool                    is_spon;
        bool                    store_hard_reset_reason;
+       bool                    kpdpwr_dbc_enable;
+       ktime_t                 kpdpwr_last_release_time;
 };
 
 static int pon_ship_mode_en;
@@ -381,7 +383,7 @@ static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay)
        int rc = 0;
        u32 val;
 
-       if (delay == pon->dbc)
+       if (delay == pon->dbc_time_us)
                goto out;
 
        if (pon->pon_input)
@@ -409,7 +411,7 @@ static int qpnp_pon_set_dbc(struct qpnp_pon *pon, u32 delay)
                goto unlock;
        }
 
-       pon->dbc = delay;
+       pon->dbc_time_us = delay;
 
 unlock:
        if (pon->pon_input)
@@ -418,12 +420,34 @@ out:
        return rc;
 }
 
+static int qpnp_pon_get_dbc(struct qpnp_pon *pon, u32 *delay)
+{
+       int rc;
+       unsigned int val;
+
+       rc = regmap_read(pon->regmap, QPNP_PON_DBC_CTL(pon), &val);
+       if (rc) {
+               pr_err("Unable to read pon_dbc_ctl rc=%d\n", rc);
+               return rc;
+       }
+       val &= QPNP_PON_DBC_DELAY_MASK(pon);
+
+       if (is_pon_gen2(pon))
+               *delay = USEC_PER_SEC /
+                       (1 << (QPNP_PON_GEN2_DELAY_BIT_SHIFT - val));
+       else
+               *delay = USEC_PER_SEC /
+                       (1 << (QPNP_PON_DELAY_BIT_SHIFT - val));
+
+       return rc;
+}
+
 static ssize_t qpnp_pon_dbc_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
        struct qpnp_pon *pon = dev_get_drvdata(dev);
 
-       return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc);
+       return snprintf(buf, QPNP_PON_BUFFER_SIZE, "%d\n", pon->dbc_time_us);
 }
 
 static ssize_t qpnp_pon_dbc_store(struct device *dev,
@@ -777,6 +801,7 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
        u8  pon_rt_bit = 0;
        u32 key_status;
        uint pon_rt_sts;
+       u64 elapsed_us;
 
        cfg = qpnp_get_cfg(pon, pon_type);
        if (!cfg)
@@ -786,6 +811,15 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
        if (!cfg->key_code)
                return 0;
 
+       if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) {
+               elapsed_us = ktime_us_delta(ktime_get(),
+                               pon->kpdpwr_last_release_time);
+               if (elapsed_us < pon->dbc_time_us) {
+                       pr_debug("Ignoring kpdpwr event - within debounce time\n");
+                       return 0;
+               }
+       }
+
        /* check the RT status to get the current status of the line */
        rc = regmap_read(pon->regmap, QPNP_PON_RT_STS(pon), &pon_rt_sts);
        if (rc) {
@@ -814,6 +848,11 @@ qpnp_pon_input_dispatch(struct qpnp_pon *pon, u32 pon_type)
                                        cfg->key_code, pon_rt_sts);
        key_status = pon_rt_sts & pon_rt_bit;
 
+       if (pon->kpdpwr_dbc_enable && cfg->pon_type == PON_KPDPWR) {
+               if (!key_status)
+                       pon->kpdpwr_last_release_time = ktime_get();
+       }
+
        /*
         * simulate press event in case release event occurred
         * without a press event
@@ -2233,8 +2272,22 @@ static int qpnp_pon_probe(struct platform_device *pdev)
                }
        } else {
                rc = qpnp_pon_set_dbc(pon, delay);
+               if (rc) {
+                       dev_err(&pdev->dev,
+                               "Unable to set PON debounce delay rc=%d\n", rc);
+                       return rc;
+               }
+       }
+       rc = qpnp_pon_get_dbc(pon, &pon->dbc_time_us);
+       if (rc) {
+               dev_err(&pdev->dev,
+                       "Unable to get PON debounce delay rc=%d\n", rc);
+               return rc;
        }
 
+       pon->kpdpwr_dbc_enable = of_property_read_bool(pon->pdev->dev.of_node,
+                                       "qcom,kpdpwr-sw-debounce");
+
        rc = of_property_read_u32(pon->pdev->dev.of_node,
                                "qcom,warm-reset-poweroff-type",
                                &pon->warm_reset_poff_type);