From patchwork Thu Feb 16 14:23:03 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Gaignard X-Patchwork-Id: 94077 Delivered-To: patch@linaro.org Received: by 10.140.20.99 with SMTP id 90csp2527991qgi; Thu, 16 Feb 2017 06:23:45 -0800 (PST) X-Received: by 10.84.218.1 with SMTP id q1mr3613441pli.104.1487255025638; Thu, 16 Feb 2017 06:23:45 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id b14si1580009plk.75.2017.02.16.06.23.45; Thu, 16 Feb 2017 06:23:45 -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=@linaro.org; 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 S932254AbdBPOXX (ORCPT + 25 others); Thu, 16 Feb 2017 09:23:23 -0500 Received: from mail-wm0-f52.google.com ([74.125.82.52]:35885 "EHLO mail-wm0-f52.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932197AbdBPOXS (ORCPT ); Thu, 16 Feb 2017 09:23:18 -0500 Received: by mail-wm0-f52.google.com with SMTP id c85so67656115wmi.1 for ; Thu, 16 Feb 2017 06:23:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=0/UyovYwL95MtaMxeXlKaYrCOmx0fqp+ib2jYKKUv+Q=; b=QfPtZZW2bQiHU2catd+hWdb004I6ejb6jWGfjL1efn7CmHZJCg60hBE1ZfTdly38zW IAwWUwsKePkOKZd2il2KuHNgZ6zih+vooRMx3o25bpeE/SYJVoh2aRABb/d9/CTcf+CA 5LPvh4nZOUxu9NwlF/Mi1LsDD80b2eAZsdOt0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=0/UyovYwL95MtaMxeXlKaYrCOmx0fqp+ib2jYKKUv+Q=; b=m4ckAkbOFEaBeMwL+4/wx0VJi6o+FlTvK9mUsIKHpLd/NaIYNYmXga550pa4KK/AgT nJLiLMBQYJrJs9MsjIOvPLDfrEAEiJ/mEyl4jxsUxQn1uqwVfJ8TGeES2mv+a1Hpmej3 oa6gXihwPgYfN8Bi2DZKCApesH7aN5MfX3ISRZUaPUZAOqH0y1RImgHR/vYtdu0KYWjk pg46ui5cA5diN8rff0e53wY3LcwKFeBu12gjW0hjpIqK4z6210gVIqhq+GMldsl69eQw B0G+gKUkCJ0T2epN7wL8DUZeFCf12tPUwABrUauX6V988q6pTpe8N1SUXSAlsZXFbCI/ 3bLg== X-Gm-Message-State: AMke39l+D9glKMGEbQtqe76DsxCqoueXXqUR+ifn46+djjRzK7/6dk4SLYfGKdV6j0+1KOM5 X-Received: by 10.28.27.69 with SMTP id b66mr12438838wmb.50.1487254997190; Thu, 16 Feb 2017 06:23:17 -0800 (PST) Received: from lmenx321.st.com. ([80.214.74.235]) by smtp.gmail.com with ESMTPSA id s26sm9137151wra.26.2017.02.16.06.23.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 16 Feb 2017 06:23:16 -0800 (PST) From: Benjamin Gaignard X-Google-Original-From: Benjamin Gaignard To: linux-kernel@vger.kernel.org, jic23@kernel.org, linux-iio@vger.kernel.org, knaack.h@gmx.de, lars@metafoo.de, pmeerw@pmeerw.net Cc: fabrice.gasnier@st.com, linaro-kernel@lists.linaro.org, benjamin.gaignard@linaro.org, Benjamin Gaignard Subject: [PATCH v2 2/2] iio: stm32 trigger: Implement parent trigger feature Date: Thu, 16 Feb 2017 15:23:03 +0100 Message-Id: <1487254984-20122-3-git-send-email-benjamin.gaignard@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1487254984-20122-1-git-send-email-benjamin.gaignard@st.com> References: <1487254984-20122-1-git-send-email-benjamin.gaignard@st.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Add validate_trigger function in iio_trigger_ops and dev_attr_parent_trigger into trigger attribute group to be able to accept triggers as parents. Because the hardware have 8 different ways to use parent levels and edges, this patch introduce "slave_mode" sysfs attribute for stm32 triggers. Modes usages are described in sysfs-bus-iio-timer-stm32 Signed-off-by: Benjamin Gaignard version 2: - use dev_attr_parent_trigger - improve slave modes documentation --- .../ABI/testing/sysfs-bus-iio-timer-stm32 | 43 +++++++++ drivers/iio/trigger/stm32-timer-trigger.c | 105 +++++++++++++++++++++ 2 files changed, 148 insertions(+) -- 1.9.1 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 index 6534a60..7d667f9 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 +++ b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 @@ -27,3 +27,46 @@ Description: Reading returns the current sampling frequency. Writing an value different of 0 set and start sampling. Writing 0 stop sampling. + +What: /sys/bus/iio/devices/triggerX/slave_mode_available +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the list possible slave modes which are: + - "disabled" : Parent trigger levels or edges have do not impact on trigger. + Trigger is clocked by the internal clock. + This is the default mode. + - "encoder_1" : Trigger internal counter counts up/down on channel 2 edge depending on channel 1 level. + - "encoder_2" : Trigger internal counter counts up/down on channel 1 edge depending on channel 2 level. + - "encoder_3" : Trigger internal counter counts up/down on channels 1 & 2 edge + depending on channel 1 & 2 level. + - "reset" : Rising edge on parent trigger reinitializes the trigger and generates + an update of auto-reload, prescaler and repetition counter registers. + - "gated" : The trigger is enabled when the parent trigger input is high. + The trigger stops (but is not reset) as soon as the parent trigger becomes low. + - "trigger" : The trigger starts at a rising edge of the parent trigger (but it is not reset). + - "external_clock": Rising edges of the parent trigger clock the trigger. + + Encoder modes are used to automatically handle the counting direction of the internal counter. + Those modes are typically used for high-accuracy rotor position sensing in electrical motors + or for digital potentiometers. From the two outputs of a quadrature encoder sensor the timer + extracts a clock on each and every active edge and adjusts the counting direction depending on + the relative phase-shift between the two incomings signals. The timer counter thus directly + holds the angular position of the motor or the potentionmeter. + + For "reset", "gated" and "trigger" modes the trigger will fire N+1 times when internal counter + will reach the value of auto-reload register. N is defined by the value of repetition counter. + Those modes could allow parent trigger to control when sampling frequency of the current trigger + start or stop. + Since PWM and trigger features are mixed in the same hardware block those 3 modes could be used + to synchronize PWMs start while PWM sysfs API is used to set period and duty cycle. + + In "external clock" mode parent trigger can control the current trigger clock (and so the sampling + frequency) for example to correct jittering. + +What: /sys/bus/iio/devices/triggerX/slave_mode +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the current slave mode. + Writing set the slave mode diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 994b96d..a4f1061 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -15,6 +15,7 @@ #include #define MAX_TRIGGERS 6 +#define MAX_VALIDS 5 /* List the triggers created by each timer */ static const void *triggers_table[][MAX_TRIGGERS] = { @@ -32,12 +33,29 @@ { TIM12_TRGO, TIM12_CH1, TIM12_CH2,}, }; +/* List the triggers accepted by each timer */ +static const void *valids_table[][MAX_VALIDS] = { + { TIM5_TRGO, TIM2_TRGO, TIM4_TRGO, TIM3_TRGO,}, + { TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,}, + { TIM1_TRGO, TIM8_TRGO, TIM5_TRGO, TIM4_TRGO,}, + { TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,}, + { TIM2_TRGO, TIM3_TRGO, TIM4_TRGO, TIM8_TRGO,}, + { }, /* timer 6 */ + { }, /* timer 7 */ + { TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,}, + { TIM2_TRGO, TIM3_TRGO,}, + { }, /* timer 10 */ + { }, /* timer 11 */ + { TIM4_TRGO, TIM5_TRGO,}, +}; + struct stm32_timer_trigger { struct device *dev; struct regmap *regmap; struct clk *clk; u32 max_arr; const void *triggers; + const void *valids; }; static int stm32_timer_start(struct stm32_timer_trigger *priv, @@ -221,10 +239,66 @@ static IIO_DEVICE_ATTR(master_mode, 0660, stm32_tt_store_master_mode, 0); +static char *slave_mode_table[] = { + "disabled", + "encoder_1", + "encoder_2", + "encoder_3", + "reset", + "gated", + "trigger", + "external_clock", +}; + +static ssize_t stm32_tt_show_slave_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 smcr; + + regmap_read(priv->regmap, TIM_SMCR, &smcr); + smcr &= TIM_SMCR_SMS; + + return snprintf(buf, PAGE_SIZE, "%s\n", slave_mode_table[smcr]); +} + +static ssize_t stm32_tt_store_slave_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + int i; + + for (i = 0; i < ARRAY_SIZE(slave_mode_table); i++) { + if (!strncmp(slave_mode_table[i], buf, + strlen(slave_mode_table[i]))) { + regmap_update_bits(priv->regmap, + TIM_SMCR, TIM_SMCR_SMS, i); + return len; + } + } + + return -EINVAL; +} + +static IIO_CONST_ATTR(slave_mode_available, +"disabled encoder_1 encoder_2 encoder_3 reset gated trigger external_clock"); + +static IIO_DEVICE_ATTR(slave_mode, 0660, + stm32_tt_show_slave_mode, + stm32_tt_store_slave_mode, + 0); + static struct attribute *stm32_trigger_attrs[] = { &iio_dev_attr_sampling_frequency.dev_attr.attr, &iio_dev_attr_master_mode.dev_attr.attr, &iio_const_attr_master_mode_available.dev_attr.attr, + &iio_dev_attr_slave_mode.dev_attr.attr, + &iio_const_attr_slave_mode_available.dev_attr.attr, + &dev_attr_parent_trigger.attr, NULL, }; @@ -237,8 +311,38 @@ static IIO_DEVICE_ATTR(master_mode, 0660, NULL, }; +static int stm32_validate_trigger(struct iio_trigger *trig, + struct iio_trigger *parent) +{ + struct stm32_timer_trigger *priv = iio_trigger_get_drvdata(trig); + const char * const *cur = priv->valids; + unsigned int i = 0; + + if (!parent) { + regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_TS, 0); + return 0; + } + + if (!is_stm32_timer_trigger(parent)) + return -EINVAL; + + while (cur && *cur) { + if (!strncmp(parent->name, *cur, strlen(parent->name))) { + regmap_update_bits(priv->regmap, + TIM_SMCR, TIM_SMCR_TS, + i << TIM_SMCR_TS_SHIFT); + return 0; + } + cur++; + i++; + } + + return -EINVAL; +} + static const struct iio_trigger_ops timer_trigger_ops = { .owner = THIS_MODULE, + .validate_trigger = stm32_validate_trigger, }; static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) @@ -312,6 +416,7 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; priv->triggers = triggers_table[index]; + priv->valids = valids_table[index]; ret = stm32_setup_iio_triggers(priv); if (ret)