Received: by 2002:ac0:a679:0:0:0:0:0 with SMTP id p54csp781531imp; Wed, 20 Feb 2019 08:55:22 -0800 (PST) X-Google-Smtp-Source: AHgI3IZUqfl0TkcqXW7cjGDT2LQI9jEDiPl8Cy6x49+BfNp7/z4TSivXT9i+7aB6b5DBEUihdk1a X-Received: by 2002:a65:628e:: with SMTP id f14mr30061731pgv.193.1550681722162; Wed, 20 Feb 2019 08:55:22 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1550681722; cv=none; d=google.com; s=arc-20160816; b=g0K3jaIQiwqw2OR5qQwy94+U/VA8mZNMOd7M6lenSnhd1c99j1jQb7l/OpXfOa+5AP XdsBEdafmrkYS0pOnCKrcWhCKYgqsfLlm7PZkJg2A4IMjWae0uDdEZazwDvBxPSNEwxx 5Zy6nu1qlZcMZ3o2PNpoh++FOu0Q6set6Tt50WhivRk+asw9Y4S943gL9fd+SMiLmrGZ HjR5C8LOMIBhDGHvimAjFQr5KGJopR4x+w9L+obUO5o8Jt3IQNrHCkVcl76PDYHp8nm1 EQ/ye7KqXhMCX7jKyMo64O96sXKWt5828J3pEUoOW0tq7DbmQcuNHaYEG7Fe9w/MecYX sdQQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:subject:cc:to:from:date :dkim-signature; bh=VUozMAKQMnKR/xUDoKQar3pT6vS85a0VN6eSPj7mt2Q=; b=I/6Xi5Z2URyZG+2DwgYmCqNhXH7DbuVvUyTDtUIPfW9XBF8obYqMlidzFHYTXo52je aX8fIYJiHKfudV6eoo9WtilIcmZPgSCQgciY+O3uuqC3TngusN3YC8JwYF0nrFLOTddg X9490YWkIdaJLwpnjhs4RtVbdH1Z+zzNI7OTEIQi5Gkq6b7yL3RbxUZ0aSqHwFMZmASK AbWoIEBkuAERvGb+gDNja4f7hstz4DwHoGN8XLvbh/NNBKKViJedcqRbs+6QtZxcmrgx BZDVH8/pEoQS6D3yzQQlAxlKeXIQ2Oj7LI0rklYgxy1plnZc57Yh4IQRVY5Z2stvdeuz qbGA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=S+FOaVGS; 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=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id m8si16501408pgp.322.2019.02.20.08.55.06; Wed, 20 Feb 2019 08:55:22 -0800 (PST) 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=@kernel.org header.s=default header.b=S+FOaVGS; 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=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726648AbfBTQyn (ORCPT + 99 others); Wed, 20 Feb 2019 11:54:43 -0500 Received: from mail.kernel.org ([198.145.29.99]:41618 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725885AbfBTQym (ORCPT ); Wed, 20 Feb 2019 11:54:42 -0500 Received: from archlinux (cpc91196-cmbg18-2-0-cust659.5-4.cable.virginm.net [81.96.234.148]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 0F6C121841; Wed, 20 Feb 2019 16:54:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1550681681; bh=XJLxIoLyKdGaSW3pIVrH0HtLNpF/mN2UfFqH+cyMEHE=; h=Date:From:To:Cc:Subject:In-Reply-To:References:From; b=S+FOaVGSB3cdQuss4KT1kYtWT/cZFEOJNEaYl5BawmdMU+V8tLzOEMcLnuw6z54OA 3A4hcTkLrIQj+fMZyPY9CadJH78n9SWdRGSgxujPMOKupsWQl4PSyG86lyoBSs1zpW b7MoZZjkppOImywIdriYVuf35I/akfDXgiZNysZ4= Date: Wed, 20 Feb 2019 16:54:33 +0000 From: Jonathan Cameron To: Patrick Havelange Cc: Hartmut Knaack , Lars-Peter Clausen , Peter Meerwald-Stadler , Rob Herring , Mark Rutland , Shawn Guo , Li Yang , Daniel Lezcano , Thomas Gleixner , Thierry Reding , Esben Haabendal , William Breathitt Gray , Linus Walleij , linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pwm@vger.kernel.org, linuxppc-dev@lists.ozlabs.org Subject: Re: [PATCH 8/8] iio/counter/ftm-quaddec: add handling of under/overflow of the counter. Message-ID: <20190220165433.22f5d58c@archlinux> In-Reply-To: <20190218140321.19166-8-patrick.havelange@essensium.com> References: <20190218140321.19166-1-patrick.havelange@essensium.com> <20190218140321.19166-8-patrick.havelange@essensium.com> X-Mailer: Claws Mail 3.17.3 (GTK+ 2.24.32; x86_64-pc-linux-gnu) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Mon, 18 Feb 2019 15:03:21 +0100 Patrick Havelange wrote: > This is implemented by polling the counter value. A new parameter > "poll-interval" can be set in the device tree, or can be changed > at runtime. The reason for the polling is to avoid interrupts flooding. > If the quadrature input is going up and down around the overflow value > (or around 0), the interrupt will be triggering all the time. Thus, > polling is an easy way to handle overflow in a consistent way. > Polling can still be disabled by setting poll-interval to 0. > > Signed-off-by: Patrick Havelange > Reviewed-by: Esben Haabendal Comments inline. Jonathan > --- > drivers/iio/counter/ftm-quaddec.c | 199 +++++++++++++++++++++++++++++- > 1 file changed, 193 insertions(+), 6 deletions(-) > > diff --git a/drivers/iio/counter/ftm-quaddec.c b/drivers/iio/counter/ftm-quaddec.c > index ca7e55a9ab3f..3a0395c3ef33 100644 > --- a/drivers/iio/counter/ftm-quaddec.c > +++ b/drivers/iio/counter/ftm-quaddec.c > @@ -25,11 +25,33 @@ > > struct ftm_quaddec { > struct platform_device *pdev; > + struct delayed_work delayedcounterwork; > void __iomem *ftm_base; > bool big_endian; > + > + /* Offset added to the counter to adjust for overflows of the > + * 16 bit HW counter. Only the 16 MSB are set. Comment syntax. > + */ > + uint32_t counteroffset; > + > + /* Store the counter on each read, this is used to detect > + * if the counter readout if we over or underflow > + */ > + uint8_t lastregion; > + > + /* Poll-interval, in ms before delayed work must poll counter */ > + uint16_t poll_interval; > + > struct mutex ftm_quaddec_mutex; > }; > > +struct counter_result { > + /* 16 MSB are from the counteroffset > + * 16 LSB are from the hardware counter > + */ > + uint32_t value; Why the structure? > +}; > + > #define HASFLAGS(flag, bits) ((flag & bits) ? 1 : 0) > > #define DEFAULT_POLL_INTERVAL 100 /* in msec */ > @@ -74,8 +96,75 @@ static void ftm_set_write_protection(struct ftm_quaddec *ftm) > ftm_write(ftm, FTM_FMS, FTM_FMS_WPEN); > } > > +/* must be called with mutex locked */ > +static void ftm_work_reschedule(struct ftm_quaddec *ftm) > +{ > + cancel_delayed_work(&ftm->delayedcounterwork); > + if (ftm->poll_interval > 0) > + schedule_delayed_work(&ftm->delayedcounterwork, > + msecs_to_jiffies(ftm->poll_interval)); > +} > + > +/* Reports the hardware counter added the offset counter. > + * > + * The quadrature decodes does not use interrupts, because it cannot be > + * guaranteed that the counter won't flip between 0xFFFF and 0x0000 at a high > + * rate, causing Real Time performance degration. Instead the counter must be > + * read frequently enough - the assumption is 150 KHz input can be handled with > + * 100 ms read cycles. > + */ > +static void ftm_work_counter(struct ftm_quaddec *ftm, > + struct counter_result *returndata) > +{ > + /* only 16bits filled in*/ > + uint32_t hwcounter; > + uint8_t currentregion; > + > + mutex_lock(&ftm->ftm_quaddec_mutex); > + > + ftm_read(ftm, FTM_CNT, &hwcounter); > + > + /* Divide the counter in four regions: > + * 0x0000-0x4000-0x8000-0xC000-0xFFFF > + * When the hwcounter changes between region 0 and 3 there is an > + * over/underflow > + */ > + currentregion = hwcounter / 0x4000; > + > + if (ftm->lastregion == 3 && currentregion == 0) > + ftm->counteroffset += 0x10000; > + > + if (ftm->lastregion == 0 && currentregion == 3) > + ftm->counteroffset -= 0x10000; > + > + ftm->lastregion = currentregion; > + > + if (returndata) > + returndata->value = ftm->counteroffset + hwcounter; > + > + ftm_work_reschedule(ftm); > + > + mutex_unlock(&ftm->ftm_quaddec_mutex); > +} > + > +/* wrapper around the real function */ > +static void ftm_work_counter_delay(struct work_struct *workptr) > +{ > + struct delayed_work *work; > + struct ftm_quaddec *ftm; > + > + work = container_of(workptr, struct delayed_work, work); > + ftm = container_of(work, struct ftm_quaddec, delayedcounterwork); > + > + ftm_work_counter(ftm, NULL); > +} > + > +/* must be called with mutex locked */ > static void ftm_reset_counter(struct ftm_quaddec *ftm) > { > + ftm->counteroffset = 0; > + ftm->lastregion = 0; > + > /* Reset hardware counter to CNTIN */ > ftm_write(ftm, FTM_CNT, 0x0); > } > @@ -110,18 +199,91 @@ static int ftm_quaddec_read_raw(struct iio_dev *indio_dev, > int *val, int *val2, long mask) > { > struct ftm_quaddec *ftm = iio_priv(indio_dev); > - uint32_t counter; > + struct counter_result counter; > > switch (mask) { > case IIO_CHAN_INFO_RAW: > - ftm_read(ftm, FTM_CNT, &counter); > - *val = counter; > + case IIO_CHAN_INFO_PROCESSED: > + ftm_work_counter(ftm, &counter); > + if (mask == IIO_CHAN_INFO_RAW) > + counter.value &= 0xffff; > + > + *val = counter.value; > + > return IIO_VAL_INT; > default: > return -EINVAL; > } > } > > +static uint32_t ftm_get_default_poll_interval(const struct ftm_quaddec *ftm) > +{ > + /* Read values from device tree */ > + uint32_t val; > + const struct device_node *node = ftm->pdev->dev.of_node; > + > + if (of_property_read_u32(node, "poll-interval", &val)) > + val = DEFAULT_POLL_INTERVAL; > + > + return val; > +} > + > +static ssize_t ftm_read_default_poll_interval(struct iio_dev *indio_dev, > + uintptr_t private, > + struct iio_chan_spec const *chan, > + char *buf) > +{ > + const struct ftm_quaddec *ftm = iio_priv(indio_dev); > + uint32_t val = ftm_get_default_poll_interval(ftm); > + > + return snprintf(buf, PAGE_SIZE, "%u\n", val); > +} > + > +static ssize_t ftm_read_poll_interval(struct iio_dev *indio_dev, > + uintptr_t private, > + struct iio_chan_spec const *chan, > + char *buf) > +{ > + struct ftm_quaddec *ftm = iio_priv(indio_dev); > + > + uint32_t poll_interval = READ_ONCE(ftm->poll_interval); Why bother with the local variable? I'm not awake enough to see why the READ_ONCE is necessary here. If worried about it, just take the lock, we are far from high performance in this path. > + > + return snprintf(buf, PAGE_SIZE, "%u\n", poll_interval); > +} > + > +static ssize_t ftm_write_poll_interval(struct iio_dev *indio_dev, > + uintptr_t private, > + struct iio_chan_spec const *chan, > + const char *buf, size_t len) > +{ > + struct ftm_quaddec *ftm = iio_priv(indio_dev); > + uint32_t newpoll_interval; > + uint32_t default_interval; > + > + if (kstrtouint(buf, 10, &newpoll_interval) != 0) { > + dev_err(&ftm->pdev->dev, "poll_interval not a number: '%s'\n", > + buf); > + return -EINVAL; > + } > + > + /* Don't accept polling times below the default value to protect the > + * system. > + */ > + default_interval = ftm_get_default_poll_interval(ftm); > + > + if (newpoll_interval < default_interval && newpoll_interval != 0) > + newpoll_interval = default_interval; > + > + mutex_lock(&ftm->ftm_quaddec_mutex); > + > + WRITE_ONCE(ftm->poll_interval, newpoll_interval); > + ftm_work_reschedule(ftm); > + > + mutex_unlock(&ftm->ftm_quaddec_mutex); > + > + return len; > +} > + > static ssize_t ftm_write_reset(struct iio_dev *indio_dev, > uintptr_t private, > struct iio_chan_spec const *chan, > @@ -135,8 +297,11 @@ static ssize_t ftm_write_reset(struct iio_dev *indio_dev, > return -EINVAL; > } > > + mutex_lock(&ftm->ftm_quaddec_mutex); > + > ftm_reset_counter(ftm); > > + mutex_unlock(&ftm->ftm_quaddec_mutex); > return len; > } > > @@ -192,6 +357,17 @@ static const struct iio_enum ftm_quaddec_prescaler_en = { > }; > > static const struct iio_chan_spec_ext_info ftm_quaddec_ext_info[] = { > + { > + .name = "default_poll_interval", > + .shared = IIO_SHARED_BY_TYPE, > + .read = ftm_read_default_poll_interval, Why is this relevant if the value is set to something else? > + }, > + { > + .name = "poll_interval", > + .shared = IIO_SHARED_BY_TYPE, > + .read = ftm_read_poll_interval, > + .write = ftm_write_poll_interval, Hmm. New ABI that needs documenting. I'm not sure how we should handle this. Perhaps William has a good suggestion for how to do it. > + }, > { > .name = "reset", > .shared = IIO_SEPARATE, > @@ -205,7 +381,8 @@ static const struct iio_chan_spec_ext_info ftm_quaddec_ext_info[] = { > static const struct iio_chan_spec ftm_quaddec_channels = { > .type = IIO_COUNT, > .channel = 0, > - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | > + BIT(IIO_CHAN_INFO_PROCESSED), Why? As a general rule, we don't allow bother RAW and processed for a particular channel. Note the raw value doesn't actually 'have' to be the value off a sensor - it is just expected to not have linear scaling and offset applied (which are encode dependent here so can't be applied). So just use raw for your non overflowing version. > .ext_info = ftm_quaddec_ext_info, > .indexed = 1, > }; > @@ -232,10 +409,14 @@ static int ftm_quaddec_probe(struct platform_device *pdev) > > ftm->pdev = pdev; > ftm->big_endian = of_property_read_bool(node, "big-endian"); > + ftm->counteroffset = 0; > + ftm->lastregion = 0; > ftm->ftm_base = of_iomap(node, 0); > if (!ftm->ftm_base) > return -EINVAL; > > + ftm->poll_interval = ftm_get_default_poll_interval(ftm); > + > indio_dev->name = dev_name(&pdev->dev); > indio_dev->dev.parent = &pdev->dev; > indio_dev->info = &ftm_quaddec_iio_info; > @@ -245,9 +426,13 @@ static int ftm_quaddec_probe(struct platform_device *pdev) > ftm_quaddec_init(ftm); > > mutex_init(&ftm->ftm_quaddec_mutex); > + INIT_DELAYED_WORK(&ftm->delayedcounterwork, ftm_work_counter_delay); > + > + ftm_work_reschedule(ftm); > > ret = devm_iio_device_register(&pdev->dev, indio_dev); > if (ret) { > + cancel_delayed_work_sync(&ftm->delayedcounterwork); > mutex_destroy(&ftm->ftm_quaddec_mutex); > iounmap(ftm->ftm_base); > } > @@ -261,13 +446,15 @@ static int ftm_quaddec_remove(struct platform_device *pdev) > > ftm = (struct ftm_quaddec *)platform_get_drvdata(pdev); > indio_dev = iio_priv_to_dev(ftm); > - /* This is needed to remove sysfs entries */ > + /* Make sure no concurrent attribute reads happen*/ > devm_iio_device_unregister(&pdev->dev, indio_dev); > > + cancel_delayed_work_sync(&ftm->delayedcounterwork); > + > ftm_write(ftm, FTM_MODE, 0); > > - iounmap(ftm->ftm_base); > mutex_destroy(&ftm->ftm_quaddec_mutex); > + iounmap(ftm->ftm_base); Why the reorder? If it was wrong in the first place, fix the earlier patch not this one. > > return 0; > }