From patchwork Fri Feb 24 14:48:15 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Benjamin Gaignard X-Patchwork-Id: 94482 Delivered-To: patch@linaro.org Received: by 10.140.20.99 with SMTP id 90csp721985qgi; Fri, 24 Feb 2017 06:49:44 -0800 (PST) X-Received: by 10.84.141.1 with SMTP id 1mr4405145plu.178.1487947784864; Fri, 24 Feb 2017 06:49:44 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id i70si7518075pge.419.2017.02.24.06.49.44; Fri, 24 Feb 2017 06:49:44 -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 S1751407AbdBXOtn (ORCPT + 25 others); Fri, 24 Feb 2017 09:49:43 -0500 Received: from mail-wm0-f48.google.com ([74.125.82.48]:36300 "EHLO mail-wm0-f48.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751315AbdBXOtT (ORCPT ); Fri, 24 Feb 2017 09:49:19 -0500 Received: by mail-wm0-f48.google.com with SMTP id v77so16186218wmv.1 for ; Fri, 24 Feb 2017 06:49: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=tRPWA/0d8q7l/HhXvHOPKNDp/iNCZfRK8Rt3WHV1bmo=; b=j65lOWKrOGZK9abpCZZUH22HDjQzuqFZOfw1ZoQGHlchkcvsYjM2vxKL0xFmL3Wo0O JYSu/76Bp54rtQtyTIamkQ0+Of65bhJ7lpeoGCuxPvrhR+iF8TwHsR9iNIIbiaF3BObk Jb/ex6AfuccIcva1maDDW2P3ZIFNfsSC+aY4g= 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=tRPWA/0d8q7l/HhXvHOPKNDp/iNCZfRK8Rt3WHV1bmo=; b=CETKdGe8v40ndROcNl+rWshcUyuPx+Qj64iwqfXEnsIZh2E55FnHNTi+f6q9kp7zQR jWw6S2o7qV0JswfJjb2rshCpxf8tgB57eiceDLjs8neytDwzjHjAowT3jKS5JtU/fFFr DWk6SnIkjzs93c3O+GMna6ZDIMF0huKXIm4iFTMQRWH4/3IY361GBmSlep8I0U1fA+cF aZY4y/M3LXiNYaTHYU5+A8tCi+8S63uLsa94cde+wfHyq74BUEx/JUBBQbV6LVt0fvIz e5q7bOhlYq1RVAkFglAHdjlwhxQdsKsvP46pC1AzrD6sX+5bEVqvUcWp7foFFno3uu7T ibyw== X-Gm-Message-State: AMke39n0us4Y7dZUtWxC17AXOlZQLNSifVO5s0P710oyY/Vgu4mukgJAEVJwyNDkHhyZUUnB X-Received: by 10.28.31.130 with SMTP id f124mr2949703wmf.130.1487947757784; Fri, 24 Feb 2017 06:49:17 -0800 (PST) Received: from lmenx321.st.com. ([80.215.37.106]) by smtp.gmail.com with ESMTPSA id y65sm2630703wmb.5.2017.02.24.06.49.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 24 Feb 2017 06:49:17 -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 v3 2/2] iio: stm32 trigger: Implement parent trigger feature Date: Fri, 24 Feb 2017 15:48:15 +0100 Message-Id: <1487947695-9305-3-git-send-email-benjamin.gaignard@st.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1487947695-9305-1-git-send-email-benjamin.gaignard@st.com> References: <1487947695-9305-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. Introduce an IIO device with one IIO_COUNT channel to get access to counter value. Counter directions can be set/get when writing/reading /sys/bus/iio/devices/iio:deviceX/in_count0_count_direction. The hardware have multiple way to use inputs edges and levels to performing counting, those different modes are exposed in: /sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available. "trigger_rising_edges" mode is automatically set when a valid parent trigger is set. Signed-off-by: Benjamin Gaignard version 2: - use dev_attr_parent_trigger - improve slave modes documentation version 3: - add one channel to get counter raw value --- .../ABI/testing/sysfs-bus-iio-timer-stm32 | 63 +++++ drivers/iio/trigger/stm32-timer-trigger.c | 256 ++++++++++++++++++++- include/linux/mfd/stm32-timers.h | 2 + 3 files changed, 315 insertions(+), 6 deletions(-) -- 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..63852a9 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 +++ b/Documentation/ABI/testing/sysfs-bus-iio-timer-stm32 @@ -27,3 +27,66 @@ 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/iio:deviceX/in_count0_raw +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns counter current value. + +What: /sys/bus/iio/devices/iio:deviceX/in_count_count_direction_available +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the list of possible counting directions which + are: + - "up" : counter is increasing. + - "down": counter is decreasing. + +What: /sys/bus/iio/devices/iio:deviceX/in_count0_count_direction +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the counting directions: + - "up" : counter is increasing. + - "down": counter is decreasing. + Writing set the counting direction. + +What: /sys/bus/iio/devices/iio:deviceX/in_count_count_mode_available +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the list of possible counting modes which are: + - "internal_clock": Counter is clocked by the internal clock rising edges. + - "encoder_1" : Counter counts up/down on channel 2 edge depending on channel 1 level. + - "encoder_2" : Counter counts up/down on channel 1 edge depending on channel 2 level. + - "encoder_3" : Counter counts up/down on channels 1 & 2 edge depending on channel 1 & 2 level. + - "reset" : Rising edges on parent trigger reinitializes the counter value to preset value. + - "gated" : Counter counts when the parent trigger input is high. + Counter stops (but is not reset) as soon as the parent trigger becomes low. + - "trigger" : Counter starts at a rising edge of the parent trigger (but it is not reset). + - "trigger_rising_edges": Rising edges of the parent trigger are used as clock by the counter. + + 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. + + "trigger_rising_edges" mode is automatically set when a valid parent trigger is set. + +What: /sys/bus/iio/devices/iio:deviceX/in_count0_count_mode +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the current counting mode. + +What: /sys/bus/iio/devices/iio:deviceX/in_count0_preset +KernelVersion: 4.12 +Contact: benjamin.gaignard@st.com +Description: + Reading returns the current preset value. + Writing set the preset value. + When counting up the counter start from 0 and fire an event when reach preset value. + When counting down the counter start from preset value and fire event when reach 0. diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 994b96d..04f5f05 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, TIM3_TRGO, TIM4_TRGO,}, + { TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,}, + { TIM1_TRGO, TIM2_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, @@ -180,8 +198,7 @@ static ssize_t stm32_tt_show_master_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); + struct stm32_timer_trigger *priv = dev_get_drvdata(dev); u32 cr2; regmap_read(priv->regmap, TIM_CR2, &cr2); @@ -194,10 +211,10 @@ static ssize_t stm32_tt_store_master_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); + struct stm32_timer_trigger *priv = dev_get_drvdata(dev); int i; + for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) { if (!strncmp(master_mode_table[i], buf, strlen(master_mode_table[i]))) { @@ -225,6 +242,7 @@ static IIO_DEVICE_ATTR(master_mode, 0660, &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, + &dev_attr_parent_trigger.attr, NULL, }; @@ -237,8 +255,49 @@ 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); + + /* + * By default make parent trigger rising edges + * use as clock for the counter + */ + regmap_update_bits(priv->regmap, TIM_SMCR, + TIM_SMCR_SMS, TIM_SMCR_SMS); + + /* Enable controller */ + regmap_update_bits(priv->regmap, TIM_CR1, + TIM_CR1_CEN, TIM_CR1_CEN); + 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) @@ -275,6 +334,185 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv) return 0; } +static int stm32_trigger_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + { + u32 cnt; + + regmap_read(priv->regmap, TIM_CNT, &cnt); + *val = cnt; + + return IIO_VAL_INT; + } + } + + return -EINVAL; +} + +static const struct iio_info stm32_trigger_info = { + .driver_module = THIS_MODULE, + .read_raw = stm32_trigger_read_raw, +}; + +static const char *const stm32_count_modes[] = { + "internal_clock", + "encoder_1", + "encoder_2", + "encoder_3", + "reset", + "gated", + "trigger", + "trigger_rising_edges" +}; + +static int stm32_set_count_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + + regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, mode); + + return 0; +} + +static int stm32_get_count_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 smcr; + + regmap_read(priv->regmap, TIM_SMCR, &smcr); + smcr &= TIM_SMCR_SMS; + + return smcr; +} + +static const struct iio_enum stm32_count_mode_enum = { + .items = stm32_count_modes, + .num_items = ARRAY_SIZE(stm32_count_modes), + .set = stm32_set_count_mode, + .get = stm32_get_count_mode +}; + +static const char *const stm32_count_direction_states[] = { + "up", + "down" +}; + +static int stm32_set_count_direction(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR, mode); + + return 0; +} + +static int stm32_get_count_direction(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 cr1; + + regmap_read(priv->regmap, TIM_CR1, &cr1); + + return (cr1 & TIM_CR1_DIR); +} + +static const struct iio_enum stm32_count_direction_enum = { + .items = stm32_count_direction_states, + .num_items = ARRAY_SIZE(stm32_count_direction_states), + .set = stm32_set_count_direction, + .get = stm32_get_count_direction +}; + +static ssize_t stm32_count_get_preset(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 arr; + + regmap_read(priv->regmap, TIM_ARR, &arr); + + return snprintf(buf, PAGE_SIZE, "%u\n", arr); +} + +static ssize_t stm32_count_set_preset(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct stm32_timer_trigger *priv = iio_priv(indio_dev); + unsigned int preset; + int ret; + + ret = kstrtouint(buf, 0, &preset); + if (ret) + return ret; + + regmap_write(priv->regmap, TIM_ARR, preset); + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); + + return len; +} + +static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { + { + .name = "preset", + .shared = IIO_SEPARATE, + .read = stm32_count_get_preset, + .write = stm32_count_set_preset + }, + IIO_ENUM("count_direction", IIO_SEPARATE, &stm32_count_direction_enum), + IIO_ENUM_AVAILABLE("count_direction", &stm32_count_direction_enum), + IIO_ENUM("count_mode", IIO_SEPARATE, &stm32_count_mode_enum), + IIO_ENUM_AVAILABLE("count_mode", &stm32_count_mode_enum), + {} +}; + +static const struct iio_chan_spec stm32_trigger_channel = { + .type = IIO_COUNT, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .ext_info = stm32_trigger_count_info, + .indexed = 1 +}; + +static struct stm32_timer_trigger *stm32_setup_iio_device(struct device *dev) +{ + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, + sizeof(struct stm32_timer_trigger)); + if (!indio_dev) + return NULL; + + indio_dev->name = dev_name(dev); + indio_dev->dev.parent = dev; + indio_dev->info = &stm32_trigger_info; + indio_dev->num_channels = 1; + indio_dev->channels = &stm32_trigger_channel; + indio_dev->dev.of_node = dev->of_node; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return NULL; + + return iio_priv(indio_dev); +} + /** * is_stm32_timer_trigger * @trig: trigger to be checked @@ -299,10 +537,15 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) if (of_property_read_u32(dev->of_node, "reg", &index)) return -EINVAL; - if (index >= ARRAY_SIZE(triggers_table)) + if (index >= ARRAY_SIZE(triggers_table) + || index >= ARRAY_SIZE(valids_table)) return -EINVAL; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + /* Create an IIO device only if we have triggers to be validated */ + if (*valids_table[index]) + priv = stm32_setup_iio_device(dev); + else + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -312,6 +555,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) diff --git a/include/linux/mfd/stm32-timers.h b/include/linux/mfd/stm32-timers.h index d030004..4a0abbc 100644 --- a/include/linux/mfd/stm32-timers.h +++ b/include/linux/mfd/stm32-timers.h @@ -21,6 +21,7 @@ #define TIM_CCMR1 0x18 /* Capt/Comp 1 Mode Reg */ #define TIM_CCMR2 0x1C /* Capt/Comp 2 Mode Reg */ #define TIM_CCER 0x20 /* Capt/Comp Enable Reg */ +#define TIM_CNT 0x24 /* Counter */ #define TIM_PSC 0x28 /* Prescaler */ #define TIM_ARR 0x2c /* Auto-Reload Register */ #define TIM_CCR1 0x34 /* Capt/Comp Register 1 */ @@ -30,6 +31,7 @@ #define TIM_BDTR 0x44 /* Break and Dead-Time Reg */ #define TIM_CR1_CEN BIT(0) /* Counter Enable */ +#define TIM_CR1_DIR BIT(4) /* Counter Direction */ #define TIM_CR1_ARPE BIT(7) /* Auto-reload Preload Ena */ #define TIM_CR2_MMS (BIT(4) | BIT(5) | BIT(6)) /* Master mode selection */ #define TIM_SMCR_SMS (BIT(0) | BIT(1) | BIT(2)) /* Slave mode selection */