Received: by 2002:a25:8b12:0:0:0:0:0 with SMTP id i18csp2231079ybl; Thu, 29 Aug 2019 05:33:58 -0700 (PDT) X-Google-Smtp-Source: APXvYqzLC7h8+b+LOGYuXb4in40EYvJ/f99mK8xETu9lisB+6vmnMaUpPVGsJ0QUmKx3LKt74na1 X-Received: by 2002:a17:902:6687:: with SMTP id e7mr9813859plk.211.1567082037997; Thu, 29 Aug 2019 05:33:57 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1567082037; cv=none; d=google.com; s=arc-20160816; b=XOrre1BYu8FDr9LnZ3MAuTC2XTR75SO96ubJw0CfN7TAqGMZbjGKQN8EXQqxwfNTR9 JRZ08jlE4HynuF5OSIvlwlKd+0kvPHv2CZ23Wa/AiMe/J2aLp7OiVhwZFgwwfjD4T5zK KTjIbfXqr/SppPkLuhQNdqAaf1OEfTXCvdyDeu1UMKVtXDtyYSK3Hw74vGZkwBYi2QKH 29/24j/mUfW12PldHfu+WvqwxsfBgv2t3Rq5vWbU7SqyTyR5bBKmBepPnudMc+9Cdi/n Cywok631BCDtKqBp5jFutGtggkQkJ3ziJFpTUeuZn0IV0dba9SDSuaRvOrdOi/aLolsB QHAA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:dkim-signature; bh=Uu2G2QOPOreVQBYbh48JdKdig/amaHs7QHKHg4v2pm4=; b=L8fmfFeF6WHorZ3VoEBtsK9aFEiUwEB/vEvq776yt5X3yoj6e4Xdf+9hIVrM1x8FAJ kRNfTD/DP4x+BBdFdwjIrV+2D3VMM/rCzEH7PRfmCR5LYiMg/dtPP5tbNTNCUS4NZ3Pm Rh2xdzhdrqW/ACZ0fbEnuvQOQwLlwevyO/PrphRlXc+ngC21UNV4OkIEnzZ2/oEVQrPe VZ0admts5BGucKwmMyDLLbQXx/WsijTSXrqebfrkitqkywFvNXq0rHwo+OAfxpj2RjPQ kOcdxxuE2INU2Od0aO8O6Xj1DEC239laojVRo1+hwnVMJjD5V9SPglLtmjbii1tYMlPp HYdg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=SpqfwohG; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id h31si1820547pgb.67.2019.08.29.05.33.40; Thu, 29 Aug 2019 05:33:57 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=SpqfwohG; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727034AbfH2MbM (ORCPT + 99 others); Thu, 29 Aug 2019 08:31:12 -0400 Received: from mail-qk1-f196.google.com ([209.85.222.196]:39343 "EHLO mail-qk1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726214AbfH2MbM (ORCPT ); Thu, 29 Aug 2019 08:31:12 -0400 Received: by mail-qk1-f196.google.com with SMTP id 4so2720548qki.6 for ; Thu, 29 Aug 2019 05:31:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=Uu2G2QOPOreVQBYbh48JdKdig/amaHs7QHKHg4v2pm4=; b=SpqfwohGI13rfR/BUQnzwhTbeVo7a+JKcvxN5XkSTTZnqh/1Fkmtxx1IUyyY1PtNR/ 1oY3oRTVkVrvN65Xhnwu70t+yyiJTOGVyzZ3wfm+xrdBm/KquWeoDqH0DHYAKTZ8HcPo lOOwCa9xKjiu9dt09Ry5vvaiOM35ZWFo/RZ4Sl812CyxM/0e4LlNUxMv2jBi+xlaePte xichSdzQJ2xx4fo7GyWInkM36Vi8Ei0b7vjBTwpPecuFglDqm/mvM5jIvZTFnql0bxQ8 5RT6Shg9Z2OLDp1u68GOOInByJurPRyaVqakVYd4jsOCbuqI3lds2y555cduZstaEtwC cPhQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=Uu2G2QOPOreVQBYbh48JdKdig/amaHs7QHKHg4v2pm4=; b=kRVuynhKwZRTB73Pc9j3Ikgh9G3SBSlBjHayQrfBZrh5KyjeORIvhOCMBloexDkhSP MthRTnU0WxvzG0isVjXoqVtiJoK2n7FuY34qC4Uev2r+WZid2cYpI/I1W23+4Pd2U15J f/PnAkH/W3xbK3kztPrjaIeVZAbt9uHvrTlbrBfJ6ljt90AZUTCqm2Shu6kNDKjXCa6F Xi5O1Qp8XP0Oyrwf+Y/sbw9S1ScZ1jy1wzuBFKc2S+cJS5xKKnF42A9Sy61W3bficTGJ FJhVp7fEWQvnQ+72/q+Jp48SKAmqnkXHktuGnHtqltP6BpYMOdwws6BltCGs/E1q8vso +nzA== X-Gm-Message-State: APjAAAUBGwe49gAJ53GJc2+piq5sJtfccvZlbeoaOku6tPNDiqiYfnaQ /3CDNfXAv8gRsWiL4PPlJ6dn/c11G4oZlp862i2YaQ== X-Received: by 2002:a37:4f41:: with SMTP id d62mr8767176qkb.302.1567081870439; Thu, 29 Aug 2019 05:31:10 -0700 (PDT) MIME-Version: 1.0 References: <5d66f545.1c69fb81.3663f.129d@mx.google.com> In-Reply-To: <5d66f545.1c69fb81.3663f.129d@mx.google.com> From: Amit Kucheria Date: Thu, 29 Aug 2019 18:00:59 +0530 Message-ID: Subject: Re: [PATCH v2 15/15] drivers: thermal: tsens: Add interrupt support To: Stephen Boyd Cc: Daniel Lezcano , Mark Rutland , Rob Herring , Zhang Rui , Andy Gross , Bjorn Andersson , Eduardo Valentin , linux-arm-msm , Linux Kernel Mailing List , Marc Gonzalez , Brian Masney , Linux PM list Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Thu, Aug 29, 2019 at 3:12 AM Stephen Boyd wrote: > > Quoting Amit Kucheria (2019-08-27 05:14:11) > > diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c > > index 06b44cfd5eab9..c549f8e1488ba 100644 > > --- a/drivers/thermal/qcom/tsens-common.c > > +++ b/drivers/thermal/qcom/tsens-common.c > > @@ -114,6 +146,314 @@ static int tsens_hw_to_mC(struct tsens_sensor *s, int field) > > return sign_extend32(temp, priv->tempres) * 100; > > } > > > > +/** > > + * tsens_mC_to_hw - Return correct value to be written to threshold > > + * registers, whether in ADC code or deciCelsius depending on IP version > > Document arguments and return value? Maybe summary can be 'convert > tsens temperature to hardware register value'? Fixed. > > + */ > > +static int tsens_mC_to_hw(struct tsens_sensor *s, int temp) > > +{ > > + struct tsens_priv *priv = s->priv; > > + > > + if (priv->feat->adc) { > > + /* milliC to C to adc code */ > > + return degc_to_code(temp / 1000, s); > > + } > > + > > + /* milliC to deciC */ > > + return temp / 100; > > +} > > + > > +static inline unsigned int tsens_ver(struct tsens_priv *priv) > > Can this return the enum instead of unsigned int? Fixed. > > +{ > > + return priv->feat->ver_major; > > +} > > + > > +/** > > + * tsens_set_interrupt_v1 - Disable an interrupt (enable = false) > > + * Re-enable an interrupt (enable = true) > > + */ > > +static void tsens_set_interrupt_v1(struct tsens_priv *priv, u32 hw_id, > > + enum tsens_irq_type irq_type, bool enable) > > +{ > > + u32 index; > > + > > + if (enable) { > > + switch (irq_type) { > > + case UPPER: > > + index = UP_INT_CLEAR_0 + hw_id; > > + break; > > + case LOWER: > > + index = LOW_INT_CLEAR_0 + hw_id; > > + break; > > + } > > + regmap_field_write(priv->rf[index], 0); > > + } else { > > + switch (irq_type) { > > + case UPPER: > > + index = UP_INT_CLEAR_0 + hw_id; > > + break; > > + case LOWER: > > + index = LOW_INT_CLEAR_0 + hw_id; > > + break; > > + } > > + regmap_field_write(priv->rf[index], 1); > > + } > > De-dup the switch statement and have > > regmap_field_write(priv->rf[index], enable ? 1 : 0); Fixed. > > +} > > + > > +/** > > + * tsens_set_interrupt_v2 - Disable an interrupt (enable = false) > > + * Re-enable an interrupt (enable = true) > > + */ > > +static void tsens_set_interrupt_v2(struct tsens_priv *priv, u32 hw_id, > > + enum tsens_irq_type irq_type, bool enable) > > +{ > > + u32 index_mask, index_clear; > > + > > + if (enable) { > > + switch (irq_type) { > > + case UPPER: > > + index_mask = UP_INT_MASK_0 + hw_id; > > + break; > > + case LOWER: > > + index_mask = LOW_INT_MASK_0 + hw_id; > > + break; > > + } > > + regmap_field_write(priv->rf[index_mask], 0); > > + } else { > > + /* To disable the interrupt flag for a sensor: > > Nitpick: Wrong comment style. > Fixed. > > + * 1. Mask further interrupts for this sensor > > + * 2. Write 1 followed by 0 to clear the interrupt > > + */ > > + switch (irq_type) { > > + case UPPER: > > + index_mask = UP_INT_MASK_0 + hw_id; > > + index_clear = UP_INT_CLEAR_0 + hw_id; > > + break; > > + case LOWER: > > + index_mask = LOW_INT_MASK_0 + hw_id; > > + index_clear = LOW_INT_CLEAR_0 + hw_id; > > + break; > > + } > > Please extract index_mask and index_clear assignments to one switch > statement and then change the sequence to an if/else > > if (enable) { > regmap_field_write(priv->rf[index_mask], 1); > regmap_field_write(priv->rf[index_clear], 1); > regmap_field_write(priv->rf[index_clear], 0); > } else { > regmap_field_write(priv->rf[index_mask], 0); > } Fixed. > > +} > > + > > +/** > > + * tsens_set_interrupt - Disable an interrupt (enable = false) > > + * Re-enable an interrupt (enable = true) > > + */ > > +static void tsens_set_interrupt(struct tsens_priv *priv, u32 hw_id, > > + enum tsens_irq_type irq_type, bool enable) > > +{ > > + dev_dbg(priv->dev, "[%u] %s: %s -> %s\n", hw_id, __func__, > > + irq_type ? ((irq_type == 1) ? "UP" : "CRITICAL") : "LOW", > > + enable ? "en" : "dis"); > > + if (tsens_ver(priv) > VER_1_X) > > + tsens_set_interrupt_v2(priv, hw_id, irq_type, enable); > > + else > > + tsens_set_interrupt_v1(priv, hw_id, irq_type, enable); > > +} > > + > > +/** > > + * tsens_threshold_violated - Check if a sensor temperature violated a preset threshold > > + * > > Document arguments? Fixed. > > + * Return: 0 if threshold was not violated, 1 if it was violated and negative > > + * errno in case of errors > > + */ > > +static int tsens_threshold_violated(struct tsens_priv *priv, u32 hw_id, > > + struct tsens_irq_data *d) > > +{ > > + int ret; > > + > > + ret = regmap_field_read(priv->rf[UPPER_STATUS_0 + hw_id], &d->up_viol); > > + if (ret) > > + return ret; > > + ret = regmap_field_read(priv->rf[LOWER_STATUS_0 + hw_id], &d->low_viol); > > + if (ret) > > + return ret; > > + if (d->up_viol || d->low_viol) > > + return 1; > > + > > + return 0; > > +} > > + > > +static int tsens_read_irq_state(struct tsens_priv *priv, u32 hw_id, > > + struct tsens_sensor *s, struct tsens_irq_data *d) > > +{ > > + int ret; > > + > > + ret = regmap_field_read(priv->rf[UP_INT_CLEAR_0 + hw_id], &d->up_irq_clear); > > + if (ret) > > + return ret; > > + ret = regmap_field_read(priv->rf[LOW_INT_CLEAR_0 + hw_id], &d->low_irq_clear); > > + if (ret) > > + return ret; > > + if (tsens_ver(priv) > VER_1_X) { > > + ret = regmap_field_read(priv->rf[UP_INT_MASK_0 + hw_id], &d->up_irq_mask); > > + if (ret) > > + return ret; > > + ret = regmap_field_read(priv->rf[LOW_INT_MASK_0 + hw_id], &d->low_irq_mask); > > + if (ret) > > + return ret; > > + } else { > > + /* No mask register on older TSENS */ > > + d->up_irq_mask = 0; > > + d->low_irq_mask = 0; > > + } > > + > > + d->up_thresh = tsens_hw_to_mC(s, UP_THRESH_0 + hw_id); > > + d->low_thresh = tsens_hw_to_mC(s, LOW_THRESH_0 + hw_id); > > + > > + dev_dbg(priv->dev, "[%u] %s%s: status(%u|%u) | clr(%u|%u) | mask(%u|%u)\n", > > + hw_id, __func__, (d->up_viol || d->low_viol) ? "(V)" : "", > > + d->low_viol, d->up_viol, d->low_irq_clear, d->up_irq_clear, > > + d->low_irq_mask, d->up_irq_mask); > > + dev_dbg(priv->dev, "[%u] %s%s: thresh: (%d:%d)\n", hw_id, __func__, > > + (d->up_viol || d->low_viol) ? "(violation)" : "", > > + d->low_thresh, d->up_thresh); > > + > > + return 0; > > +} > > + > > +static inline u32 masked_irq(u32 hw_id, u32 mask, enum tsens_ver ver) > > +{ > > + if (ver > VER_1_X) > > + return mask & (1 << hw_id); > > + > > + /* v1, v0.1 don't have a irq mask register */ > > + return 0; > > +} > > + > > +irqreturn_t tsens_irq_thread(int irq, void *data) > > +{ > > + struct tsens_priv *priv = data; > > + struct tsens_irq_data d; > > + bool enable = true, disable = false; > > + unsigned long flags; > > + int temp, ret, i; > > + > > + /* > > + * Check if any sensor raised an IRQ - for each sensor connected to the > > + * TSENS block if it set the threshold violation bit. > > + */ > > + for (i = 0; i < priv->num_sensors; i++) { > > + bool trigger = 0; > > How about trigger = false? It's a bool. Fixed. > > + struct tsens_sensor *s = &priv->sensor[i]; > > + u32 hw_id = s->hw_id; > > + > > + if (IS_ERR(priv->sensor[i].tzd)) > > + continue; > > + if (!tsens_threshold_violated(priv, hw_id, &d)) > > + continue; > > + ret = get_temp_tsens_valid(s, &temp); > > + if (ret) { > > + dev_err(priv->dev, "[%u] %s: error reading sensor\n", hw_id, __func__); > > I hope there isn't an interrupt storm where we're trying to print out > messages from the irq handler. > > > + continue; > > + } > > + > > + spin_lock_irqsave(&priv->ul_lock, flags); > > + > > + tsens_read_irq_state(priv, hw_id, s, &d); > > + > > + if (d.up_viol && > > + !masked_irq(hw_id, d.up_irq_mask, tsens_ver(priv))) { > > + tsens_set_interrupt(priv, hw_id, UPPER, disable); > > + if (d.up_thresh > temp) { > > + dev_dbg(priv->dev, "[%u] %s: re-arm upper\n", > > + priv->sensor[i].hw_id, __func__); > > + /* unmask the interrupt for this sensor */ > > + tsens_set_interrupt(priv, hw_id, UPPER, enable); > > + } else { > > + trigger = 1; > > + /* Keep irq masked */ > > + } > > + } else if (d.low_viol && > > + !masked_irq(hw_id, d.low_irq_mask, tsens_ver(priv))) { > > + tsens_set_interrupt(priv, hw_id, LOWER, disable); > > + if (d.low_thresh < temp) { > > + dev_dbg(priv->dev, "[%u] %s: re-arm low\n", > > + priv->sensor[i].hw_id, __func__); > > + /* unmask the interrupt for this sensor */ > > + tsens_set_interrupt(priv, hw_id, LOWER, enable); > > + } else { > > + trigger = 1; > > + /* Keep irq masked */ > > + } > > + } > > + > > + spin_unlock_irqrestore(&priv->ul_lock, flags); > > + > > + if (trigger) { > > + dev_dbg(priv->dev, "[%u] %s: TZ update trigger (%d mC)\n", > > + hw_id, __func__, temp); > > + thermal_zone_device_update(priv->sensor[i].tzd, > > + THERMAL_EVENT_UNSPECIFIED); > > + } else { > > + dev_dbg(priv->dev, "[%u] %s: no violation: %d\n", > > + hw_id, __func__, temp); > > + } > > + } > > + > > + return IRQ_HANDLED; > > Should we return IRQ_NONE in the case that the above for loop didn't > find anything in those if/else-ifs? The upper/lower interrupts are non-sticky, level-triggered. So if the temp returns to within the thresholds in the time that a IRQ was triggered and the handler scheduled, we might not see any threshold violations/interrupt bits set. It feels to me that this is a case of the IRQ being handled (automagically) instead of IRQ_NONE. The definition of IRQ_NONE[1] also seems to suggest that it should be used when the IRQ wasn't handled. But it was handled in this case (although, automatically), wasn't it? [1] https://elixir.bootlin.com/linux/latest/source/include/linux/irqreturn.h#L7 > > +} > > + > > +int tsens_set_trips(void *_sensor, int low, int high) > > +{ > > + struct tsens_sensor *s = _sensor; > > + struct tsens_priv *priv = s->priv; > > + struct device *dev = priv->dev; > > + struct tsens_irq_data d; > > + unsigned long flags; > > + int high_val, low_val, cl_high, cl_low; > > + bool enable = true; > > + u32 hw_id = s->hw_id; > > + > > + dev_dbg(dev, "[%u] %s: proposed thresholds: (%d:%d)\n", > > + hw_id, __func__, low, high); > > + > > + cl_high = clamp_val(high, -40000, 120000); > > + cl_low = clamp_val(low, -40000, 120000); > > + > > + high_val = tsens_mC_to_hw(s, cl_high); > > + low_val = tsens_mC_to_hw(s, cl_low); > > + > > + spin_lock_irqsave(&priv->ul_lock, flags); > > + > > + tsens_read_irq_state(priv, hw_id, s, &d); > > + > > + /* Write the new thresholds and clear the status */ > > + regmap_field_write(priv->rf[LOW_THRESH_0 + hw_id], low_val); > > + regmap_field_write(priv->rf[UP_THRESH_0 + hw_id], high_val); > > + tsens_set_interrupt(priv, hw_id, LOWER, enable); > > + tsens_set_interrupt(priv, hw_id, UPPER, enable); > > Just pass true? Why is there an enable local variable? Good catch. Left over from a refactor with more convoluted logic. > > + > > + spin_unlock_irqrestore(&priv->ul_lock, flags); > > + > > + dev_dbg(dev, "[%u] %s: (%d:%d)->(%d:%d)\n", > > + s->hw_id, __func__, d.low_thresh, d.up_thresh, cl_low, cl_high); > > + > > + return 0; > > +} > > + > [...] > > @@ -319,28 +659,31 @@ int __init init_common(struct tsens_priv *priv) > > ret = PTR_ERR(priv->rf[SENSOR_EN]); > > goto err_put_device; > > } > > - /* now alloc regmap_fields in tm_map */ > > - for (i = 0, j = LAST_TEMP_0; i < priv->feat->max_sensors; i++, j++) { > > - priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map, > > - priv->fields[j]); > > - if (IS_ERR(priv->rf[j])) { > > - ret = PTR_ERR(priv->rf[j]); > > - goto err_put_device; > > - } > > + priv->rf[INT_EN] = devm_regmap_field_alloc(dev, priv->tm_map, > > + priv->fields[INT_EN]); > > + if (IS_ERR(priv->rf[INT_EN])) { > > + ret = PTR_ERR(priv->rf[INT_EN]); > > + goto err_put_device; > > } > > > > - /* Save away resolution of signed temperature value for this IP */ > > - priv->tempres = priv->fields[LAST_TEMP_0].msb - priv->fields[LAST_TEMP_0].lsb; > > - > > - for (i = 0, j = VALID_0; i < priv->feat->max_sensors; i++, j++) { > > - priv->rf[j] = devm_regmap_field_alloc(dev, priv->tm_map, > > - priv->fields[j]); > > - if (IS_ERR(priv->rf[j])) { > > - ret = PTR_ERR(priv->rf[j]); > > - goto err_put_device; > > + /* This loop might need changes if enum regfield_ids is reordered */ > > + for (j = LAST_TEMP_0; j <= UP_THRESH_15; j += 16) { > > + for (i = 0; i < priv->feat->max_sensors; i++) { > > + int idx = j + i; > > + > > + priv->rf[idx] = devm_regmap_field_alloc(dev, priv->tm_map, > > + priv->fields[idx]); > > + if (IS_ERR(priv->rf[idx])) { > > + ret = PTR_ERR(priv->rf[idx]); > > + goto err_put_device; > > + } > > } > > } > > + /* Save away resolution of signed temperature value for this IP */ > > + priv->tempres = priv->fields[LAST_TEMP_0].msb - priv->fields[LAST_TEMP_0].lsb; > > Leave this where it was, i.e. before the for loop? Or is that a bug and > it doesn't actually work unless it's after the for loop? In which case, > this should go to the previous patch. I've gotten rid of this completely now. > > > > + spin_lock_init(&priv->ul_lock); > > + tsens_enable_irq(priv); > > tsens_debug_init(op); > > > > return 0; > > diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c > > index 772aa76b50e12..a4335717aeede 100644 > > --- a/drivers/thermal/qcom/tsens.c > > +++ b/drivers/thermal/qcom/tsens.c > > @@ -96,7 +99,33 @@ static int tsens_register(struct tsens_priv *priv) > > if (priv->ops->enable) > > priv->ops->enable(priv, i); > > } > > + > > + pdev = of_find_device_by_node(priv->dev->of_node); > > + if (!pdev) { > > + dev_err(&pdev->dev, "%s: device node not found in DT\n", __func__); > > Do we really need this? Maybe just bail out in silence because this > should never happen? Fixed. > > + return -ENODEV; > > + } > > + > > + irq = platform_get_irq_byname(pdev, "uplow"); > > + if (irq < 0) { > > + dev_err(&pdev->dev, "%s: missing irq in dt: uplow\n", __func__); > > You can drop the error print. I upstreamed a change to print the error > generically in the core. Fixed. > > + return irq; > > Did we need to put_device() here? Refactored to goto err_put_device > > + } > > + > > + ret = devm_request_threaded_irq(&pdev->dev, irq, > > + NULL, tsens_irq_thread, > > + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, > > + dev_name(&pdev->dev), priv); > > + if (ret) { > > + dev_err(&pdev->dev, "%s: failed to get irq\n", __func__); > > + goto err_put_device; > > + } > > + enable_irq_wake(irq); > > return 0; > > + > > +err_put_device: > > + put_device(&pdev->dev); > > + return ret;