From patchwork Thu Oct 5 18:14:38 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: William Breathitt Gray X-Patchwork-Id: 9987715 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id A18F8602B8 for ; Thu, 5 Oct 2017 18:14:59 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 8EAD128BCC for ; Thu, 5 Oct 2017 18:14:59 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 8218E28D27; Thu, 5 Oct 2017 18:14:59 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AB69B28BCC for ; Thu, 5 Oct 2017 18:14:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752064AbdJESOr (ORCPT ); Thu, 5 Oct 2017 14:14:47 -0400 Received: from mail-qt0-f196.google.com ([209.85.216.196]:36998 "EHLO mail-qt0-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751486AbdJESOp (ORCPT ); Thu, 5 Oct 2017 14:14:45 -0400 Received: by mail-qt0-f196.google.com with SMTP id 32so6282115qtp.4; Thu, 05 Oct 2017 11:14:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=SvF10c5DXUlbW4T29f+Ka2SkXDmuRgjbZD86Jz0qrNI=; b=SHCgeZ2l6ZBQVvzzp0/I32TadnVXpByDwwASn57cDN6JyH5mgBomaVpm6jW1hjWdFY NtvF6Z8UrbYlKPUtNNb6vc09KvtUBsjvMXbm5hqGZQlo66wSbfb+NXkW4vr5dBZU+GGM hcS6h5Z1doN1zLjXpO9jSqCFArXLxijvO7xcjMgEt6+vUzynrf2SgW0khVj3tOmiPcCT dnxIRVAIxh53TTOM74Jazl94C9Gz2ZWb+MrMD7nFBUPSVFiu2nbgqaskyKR6TcrLzQDZ IOelR+RoxewIIdB62PieboW/uhM+77saKtzlzgt/gF2oTFxXOWJcEE85l5Wy6u5XTEEb ZEMQ== 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=SvF10c5DXUlbW4T29f+Ka2SkXDmuRgjbZD86Jz0qrNI=; b=pPMt2amn5N/mw7l9od3Zg6hC970mEpr7ywoXK1w/YvIbEVmPB/6P21NiyFrrkaMdM9 jquzvVLrKpzKuQI8VUZ5eWnECvFo5CKWJz+WZbmSYvy602snxSjMA9ECXQoyLncrffCe RPST4qg7E0u1/MTHGMeGeAAT5NOu5+ZvYBfydjzp9XPENSo+7PQq97dsldzAWfG3ghsa lcoP0eAPcNhoH8yzLFIDlrNXwINrfZ1N4D123fmzAANMbyzGRQtJDqHrnrdPxrEbQhaD o8Io52Xuhv5xIdgPaoVfiQwLAdiMMyJD7ZJeI3H/9w14PuVs0HJvwbIRBRqylxRBY8rK Fq3g== X-Gm-Message-State: AMCzsaUPfhCGiFopuWeNbRY+Z4CwIPPVhwO6LXEGn8Qu0sJeiLe/4iuO WDnl7h75NkZm7akrMpkXGXc= X-Google-Smtp-Source: AOwi7QDcFRw9Zb7cTlLdRljXsSXSfOIcvDZPI3ydHggHDEoR7OoxQORyQJHJPfc39NPq82jNK/sbCQ== X-Received: by 10.37.58.134 with SMTP id h128mr7260156yba.267.1507227284975; Thu, 05 Oct 2017 11:14:44 -0700 (PDT) Received: from localhost (72-188-97-40.res.bhn.net. [72.188.97.40]) by smtp.gmail.com with ESMTPSA id z184sm6798063ywb.51.2017.10.05.11.14.44 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 05 Oct 2017 11:14:44 -0700 (PDT) From: William Breathitt Gray To: jic23@kernel.org, benjamin.gaignard@linaro.org, knaack.h@gmx.de, lars@metafoo.de, pmeerw@pmeerw.net Cc: linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, William Breathitt Gray Subject: [PATCH v3 5/6] iio: Add dummy counter driver Date: Thu, 5 Oct 2017 14:14:38 -0400 Message-Id: <699d043348fdb4053ed7f6f7dafb8512c7d73589.1507220144.git.vilhelm.gray@gmail.com> X-Mailer: git-send-email 2.14.1 In-Reply-To: References: Sender: linux-iio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-iio@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch introduces the dummy counter driver. The dummy counter driver serves as a reference implementation of a driver that utilizes the Generic Counter interface. Writing individual '1' and '0' characters to the Signal attributes allows a user to simulate a typical Counter Signal input stream for evaluation; the Counter will evaluate the Signal data based on the respective trigger mode for the associated Signal, and trigger the associated counter function specified by the respective function mode. The current Value value may be read, and the Value value preset by a write. Signed-off-by: William Breathitt Gray --- drivers/iio/counter/Kconfig | 15 ++ drivers/iio/counter/Makefile | 1 + drivers/iio/counter/dummy-counter.c | 293 ++++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 drivers/iio/counter/dummy-counter.c diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig index c8becfe78e28..494aed40e9c9 100644 --- a/drivers/iio/counter/Kconfig +++ b/drivers/iio/counter/Kconfig @@ -22,6 +22,21 @@ config 104_QUAD_8 The base port addresses for the devices may be configured via the base array module parameter. +config DUMMY_COUNTER + tristate "Dummy counter driver" + help + Select this option to enable the dummy counter driver. The dummy + counter driver serves as a reference implementation of a driver that + utilizes the Generic Counter interface. + + Writing individual '1' and '0' characters to the Signal attributes + allows a user to simulate a typical Counter Signal input stream for + evaluation; the Counter will evaluate the Signal data based on the + respective trigger mode for the associated Signal, and trigger the + associated counter function specified by the respective function mode. + The current Value value may be read, and the Value value preset by a + write. + config STM32_LPTIMER_CNT tristate "STM32 LP Timer encoder counter driver" depends on MFD_STM32_LPTIMER || COMPILE_TEST diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile index 1b9a896eb488..8c2ef0115426 100644 --- a/drivers/iio/counter/Makefile +++ b/drivers/iio/counter/Makefile @@ -5,4 +5,5 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o +obj-$(CONFIG_DUMMY_COUNTER) += dummy-counter.o obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o diff --git a/drivers/iio/counter/dummy-counter.c b/drivers/iio/counter/dummy-counter.c new file mode 100644 index 000000000000..6ecc9854894f --- /dev/null +++ b/drivers/iio/counter/dummy-counter.c @@ -0,0 +1,293 @@ +/* + * Dummy counter driver + * Copyright (C) 2017 William Breathitt Gray + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include + +#define DUMCNT_NUM_COUNTERS 2 +/** + * struct dumcnt - private data structure + * @counter: instance of the iio_counter + * @counts: array of accumulation values + * @states: array of input line states + */ +struct dumcnt { + struct iio_counter counter; + unsigned int counts[DUMCNT_NUM_COUNTERS]; + unsigned int states[DUMCNT_NUM_COUNTERS]; +}; + +static int dumcnt_signal_read(struct iio_counter *counter, + struct iio_counter_signal *signal, int *val, int *val2) +{ + struct dumcnt *const priv = counter->driver_data; + *val = priv->states[signal->id]; + + return IIO_VAL_INT; +} + +static int dumcnt_signal_write(struct iio_counter *counter, + struct iio_counter_signal *signal, int val, int val2) +{ + struct dumcnt *const priv = counter->driver_data; + const unsigned int id = signal->id; + const unsigned int prev_state = priv->states[id]; + struct iio_counter_value *const value = counter->values + id; + const unsigned int function_mode = value->mode; + const unsigned int trigger_mode = value->triggers[0].mode; + unsigned int triggered = 0; + + if (val && val != 1) + return -EINVAL; + + /* If no state change then just exit */ + if (prev_state == val) + return 0; + + priv->states[id] = val; + + switch (trigger_mode) { + /* "none" case */ + case 0: + return 0; + /* "rising edge" case */ + case 1: + if (!prev_state) + triggered = 1; + break; + /* "falling edge" case */ + case 2: + if (prev_state) + triggered = 1; + break; + /* "both edges" case */ + case 3: + triggered = 1; + break; + } + + /* If counter function triggered */ + if (triggered) + /* "increase" case */ + if (function_mode) + priv->counts[id]++; + /* "decrease" case */ + else + priv->counts[id]--; + + return 0; +} + +static int dumcnt_trigger_mode_set(struct iio_counter *counter, + struct iio_counter_value *value, struct iio_counter_trigger *trigger, + unsigned int mode) +{ + if (mode >= trigger->num_trigger_modes) + return -EINVAL; + + trigger->mode = mode; + + return 0; +} + +static int dumcnt_trigger_mode_get(struct iio_counter *counter, + struct iio_counter_value *value, struct iio_counter_trigger *trigger) +{ + return trigger->mode; +} + +static int dumcnt_value_read(struct iio_counter *counter, + struct iio_counter_value *value, int *val, int *val2) +{ + struct dumcnt *const priv = counter->driver_data; + + *val = priv->counts[value->id]; + + return IIO_VAL_INT; +} + +static int dumcnt_value_write(struct iio_counter *counter, + struct iio_counter_value *value, int val, int val2) +{ + struct dumcnt *const priv = counter->driver_data; + + priv->counts[value->id] = val; + + return 0; +} + +static int dumcnt_value_function_set(struct iio_counter *counter, + struct iio_counter_value *value, unsigned int mode) +{ + if (mode >= value->num_function_modes) + return -EINVAL; + + value->mode = mode; + + return 0; +} + +static int dumcnt_value_function_get(struct iio_counter *counter, + struct iio_counter_value *value) +{ + return value->mode; +} + +static const struct iio_counter_ops dumcnt_ops = { + .signal_read = dumcnt_signal_read, + .signal_write = dumcnt_signal_write, + .trigger_mode_get = dumcnt_trigger_mode_get, + .trigger_mode_set = dumcnt_trigger_mode_set, + .value_read = dumcnt_value_read, + .value_write = dumcnt_value_write, + .value_function_set = dumcnt_value_function_set, + .value_function_get = dumcnt_value_function_get +}; + +static const char *const dumcnt_function_modes[] = { + "decrease", + "increase" +}; + +#define DUMCNT_SIGNAL(_id, _name) { \ + .id = _id, \ + .name = _name \ +} + +static const struct iio_counter_signal dumcnt_signals[] = { + DUMCNT_SIGNAL(0, "Signal A"), DUMCNT_SIGNAL(1, "Signal B") +}; + +#define DUMCNT_VALUE(_id, _name) { \ + .id = _id, \ + .name = _name, \ + .mode = 0, \ + .function_modes = dumcnt_function_modes, \ + .num_function_modes = ARRAY_SIZE(dumcnt_function_modes) \ +} + +static const struct iio_counter_value dumcnt_values[] = { + DUMCNT_VALUE(0, "Count A"), DUMCNT_VALUE(1, "Count B") +}; + +static const char *const dumcnt_trigger_modes[] = { + "none", + "rising edge", + "falling edge", + "both edges" +}; + +static int dumcnt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct iio_counter_signal *signals; + const size_t num_signals = ARRAY_SIZE(dumcnt_signals); + struct iio_counter_value *values; + const size_t num_values = ARRAY_SIZE(dumcnt_values); + struct iio_counter_trigger *triggers; + int i; + struct dumcnt *dumcnt; + + signals = devm_kmalloc(dev, sizeof(dumcnt_signals), GFP_KERNEL); + if (!signals) + return -ENOMEM; + + memcpy(signals, dumcnt_signals, sizeof(dumcnt_signals)); + + values = devm_kmalloc(dev, sizeof(dumcnt_values), GFP_KERNEL); + if (!values) + return -ENOMEM; + + memcpy(values, dumcnt_values, sizeof(dumcnt_values)); + + /* Associate values with their respective signals */ + for (i = 0; i < num_values; i++) { + triggers = devm_kmalloc(dev, sizeof(*triggers), GFP_KERNEL); + if (!triggers) + return -ENOMEM; + + triggers->mode = 0; + triggers->trigger_modes = dumcnt_trigger_modes; + triggers->num_trigger_modes = ARRAY_SIZE(dumcnt_trigger_modes); + triggers->signal = &signals[i]; + + values[i].triggers = triggers; + values[i].num_triggers = 1; + } + + dumcnt = devm_kzalloc(dev, sizeof(*dumcnt), GFP_KERNEL); + if (!dumcnt) + return -ENOMEM; + + dumcnt->counter.name = dev_name(dev); + dumcnt->counter.dev = dev; + dumcnt->counter.ops = &dumcnt_ops; + dumcnt->counter.signals = signals; + dumcnt->counter.num_signals = num_signals; + dumcnt->counter.values = values; + dumcnt->counter.num_values = num_values; + dumcnt->counter.driver_data = dumcnt; + + return devm_iio_counter_register(dev, &dumcnt->counter); +} + +static struct platform_device *dumcnt_device; + +static struct platform_driver dumcnt_driver = { + .driver = { + .name = "104-quad-8" + } +}; + +static void __exit dumcnt_exit(void) +{ + platform_device_unregister(dumcnt_device); + platform_driver_unregister(&dumcnt_driver); +} + +static int __init dumcnt_init(void) +{ + int err; + + dumcnt_device = platform_device_alloc(dumcnt_driver.driver.name, -1); + if (!dumcnt_device) + return -ENOMEM; + + err = platform_device_add(dumcnt_device); + if (err) + goto err_platform_device; + + err = platform_driver_probe(&dumcnt_driver, dumcnt_probe); + if (err) + goto err_platform_driver; + + return 0; + +err_platform_driver: + platform_device_del(dumcnt_device); +err_platform_device: + platform_device_put(dumcnt_device); + return err; +} + +module_init(dumcnt_init); +module_exit(dumcnt_exit); + +MODULE_AUTHOR("William Breathitt Gray "); +MODULE_DESCRIPTION("Dummy counter driver"); +MODULE_LICENSE("GPL v2");