From patchwork Thu Dec 14 20:52:08 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: 10113301 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 D2BDB602B3 for ; Thu, 14 Dec 2017 20:52:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C54D028E8F for ; Thu, 14 Dec 2017 20:52:30 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id BA29F29160; Thu, 14 Dec 2017 20:52:30 +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.8 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED,FREEMAIL_FROM,RCVD_IN_DNSWL_HI,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 12E4028E8F for ; Thu, 14 Dec 2017 20:52:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753272AbdLNUwS (ORCPT ); Thu, 14 Dec 2017 15:52:18 -0500 Received: from mail-yb0-f194.google.com ([209.85.213.194]:44281 "EHLO mail-yb0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753209AbdLNUwQ (ORCPT ); Thu, 14 Dec 2017 15:52:16 -0500 Received: by mail-yb0-f194.google.com with SMTP id z62so4514613yba.11; Thu, 14 Dec 2017 12:52:15 -0800 (PST) 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=gXJCi/I7WWqzTIyTJmUPBL0gx3tQGHnDYZ7Nvpxz5jE=; b=EgUA3lNMisK5gRIUFX8i4+ClL8MZdn6cuZ6NLTrN9MlhtXgAqTYl87n2Z0BPF6xpEa HwBy0RnoYp0xiX8ulY+KEM9rsdE4LY7IC6Jmpl6n8UNRh7jbGWRUEP7AQ5Rz+Knx7ZJi s6GL3dbEunC8v8WM9rLIPKyIbdNXA78/i6v5pEGDXcxdozl8NyatZ7NxktFdaS8JOgNh qrcXZGsRVgrcMd0bxh0nYT7YAZ+Y7Ojah4IZ1lxOiw60hueIhjS0KzaJMA0BthezI8/0 vn07QT1ypRIG+oyEKHTPzZ3HlulFdtug665+eos5BfPHFqe0KO0p1YI35QfCy3rnZCuA vOyA== 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=gXJCi/I7WWqzTIyTJmUPBL0gx3tQGHnDYZ7Nvpxz5jE=; b=S2QWkKMTliqpyAuw/i9g9Rwm1bMNxrm83lKWc+UO/lMBKhsolyXH25VQ4bmvdqE4Vr pbQg61EqpFi699nh4XvfL0rPECqnIvBoBZHLHoOPERneX/KXWUAmdZY9b0h8jDS1+7bt n5+W3eSR4ibsY45Muwdc1P/2KmXIFArOacwXM9bXjv9ib4oYnz/KX4lq9gHH7w7QfPge 09TJwH8LEl4tl1/fZA0JR8t9Eczq7eIKunWRkV9iP4dn8sTtgv5aWr+WJjczwg0hnygD foybrzb1YOp1Tu93sxEcZRqvj3MDduae2gtO6u6HVQ/OuGkvB7SsHMbkMkM8+G5pciUk 5Hlg== X-Gm-Message-State: AKGB3mI6loOa3w00cemlPpko2GCTlMOjlI4aIGblzErjSJJ1VRTxLghe iM/FRL7ffcmsWG2ju6TNt28= X-Google-Smtp-Source: ACJfBos185Aqyok+cxhvJeLNFY+mqKOPnrN9KFAlGZpyPvBfU6XWm0svLIkW4DvdKCuf+WwtIkbtfg== X-Received: by 10.37.160.199 with SMTP id i7mr5365840ybm.386.1513284735206; Thu, 14 Dec 2017 12:52:15 -0800 (PST) Received: from localhost ([72.188.97.40]) by smtp.gmail.com with ESMTPSA id h6sm2161218ywe.31.2017.12.14.12.52.14 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 14 Dec 2017 12:52:14 -0800 (PST) From: William Breathitt Gray To: jic23@kernel.org, knaack.h@gmx.de, lars@metafoo.de, pmeerw@pmeerw.net Cc: benjamin.gaignard@linaro.org, linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, William Breathitt Gray Subject: [PATCH v4 07/11] counter: Add dummy counter driver Date: Thu, 14 Dec 2017 15:52:08 -0500 Message-Id: X-Mailer: git-send-email 2.15.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 Simple Counter interface. Writing "low" and "high" to the Signal attributes allows a user to simulate a typical Simple Counter Signal input stream for evaluation; the Counter will evaluate the Signal data based on the respective action mode for the associated Signal, and trigger the associated count function specified by the respective Count's function mode. The current Count value may be read, and the Count value preset by a write. The Count max and min attributes serve to configure respective value ceiling and value floor for each desired Count. Signed-off-by: William Breathitt Gray --- drivers/iio/counter/Kconfig | 15 ++ drivers/iio/counter/Makefile | 1 + drivers/iio/counter/dummy-counter.c | 308 ++++++++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 drivers/iio/counter/dummy-counter.c diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig index 6b9a43180d2c..9d7dae137f9c 100644 --- a/drivers/iio/counter/Kconfig +++ b/drivers/iio/counter/Kconfig @@ -30,6 +30,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 "low" and "high" to the Signal attributes allows a user to + simulate a typical Simple 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) && IIO diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile index 7450dee97446..febd2884b474 100644 --- a/drivers/iio/counter/Makefile +++ b/drivers/iio/counter/Makefile @@ -9,4 +9,5 @@ counter-$(CONFIG_COUNTER) += generic-counter.o counter-$(CONFIG_COUNTER) += simple-counter.o 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..31d7e3a7d8fa --- /dev/null +++ b/drivers/iio/counter/dummy-counter.c @@ -0,0 +1,308 @@ +/* + * 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 Simple Counter + * @counts: array of accumulation counts + * @max: array of value ceilings for each count + * @min: array of value floors for each count + * @states: array of input line states + */ +struct dumcnt { + struct simple_counter_device counter; + long counts[DUMCNT_NUM_COUNTERS]; + long max[DUMCNT_NUM_COUNTERS]; + long min[DUMCNT_NUM_COUNTERS]; + enum simple_counter_signal_level states[DUMCNT_NUM_COUNTERS]; +}; + +static int dumcnt_signal_read(struct simple_counter_device *counter, + struct simple_counter_signal *signal, + enum simple_counter_signal_level *level) +{ + struct dumcnt *const priv = counter->priv; + + *level = priv->states[signal->id]; + + return 0; +} + +static int dumcnt_signal_write(struct simple_counter_device *counter, + struct simple_counter_signal *signal, + enum simple_counter_signal_level level) +{ + struct dumcnt *const priv = counter->priv; + const int id = signal->id; + const enum simple_counter_signal_level prev_state = priv->states[id]; + struct simple_counter_count *const count = counter->counts + id; + unsigned int triggered = 0; + + /* If no state change then just exit */ + if (prev_state == level) + return 0; + + priv->states[id] = level; + + /* Check if Count function was triggered */ + switch (count->action) { + case SIMPLE_COUNTER_ACTION_NONE: + return 0; + case SIMPLE_COUNTER_ACTION_RISING_EDGE: + if (!prev_state) + triggered = 1; + break; + case SIMPLE_COUNTER_ACTION_FALLING_EDGE: + if (prev_state) + triggered = 1; + break; + case SIMPLE_COUNTER_ACTION_BOTH_EDGES: + triggered = 1; + break; + } + + /* Exit early if Count function was not triggered */ + if (!triggered) + return 0; + + /* Execute Count function */ + switch (count->function) { + case SIMPLE_COUNTER_FUNCTION_INCREASE: + if (priv->counts[id] < priv->max[id]) + priv->counts[id]++; + break; + case SIMPLE_COUNTER_FUNCTION_DECREASE: + if (priv->counts[id] > priv->min[id]) + priv->counts[id]--; + break; + } + + return 0; +} + +static int dumcnt_count_read(struct simple_counter_device *counter, + struct simple_counter_count *count, long *val) +{ + struct dumcnt *const priv = counter->priv; + + *val = priv->counts[count->id]; + + return 0; +} + +static int dumcnt_count_write(struct simple_counter_device *counter, + struct simple_counter_count *count, long val) +{ + struct dumcnt *const priv = counter->priv; + const int id = count->id; + + /* Verify that value is within configured boundaries */ + if (val > priv->max[id] || val < priv->min[id]) + return -EINVAL; + + priv->counts[id] = val; + + return 0; +} + +static int dumcnt_function_get(struct simple_counter_device *counter, + struct simple_counter_count *count, + enum simple_counter_function *function) +{ + *function = count->function; + + return 0; +} + +static int dumcnt_function_set(struct simple_counter_device *counter, + struct simple_counter_count *count, + enum simple_counter_function function) +{ + return 0; +} + +static int dumcnt_action_get(struct simple_counter_device *counter, + struct simple_counter_count *count, + enum simple_counter_action *action) +{ + *action = count->action; + + return 0; +} + +static int dumcnt_action_set(struct simple_counter_device *counter, + struct simple_counter_count *count, + enum simple_counter_action action) +{ + return 0; +} + +ssize_t dumcnt_count_max_read(struct simple_counter_device *counter, + struct simple_counter_count *count, void *priv, char *buf) +{ + struct dumcnt *const drv_data = counter->priv; + + return scnprintf(buf, PAGE_SIZE, "%ld\n", drv_data->max[count->id]); +} + +ssize_t dumcnt_count_max_write(struct simple_counter_device *counter, + struct simple_counter_count *count, void *priv, const char *buf, + size_t len) +{ + int err; + struct dumcnt *const drv_data = counter->priv; + + err = kstrtol(buf, 0, drv_data->max + count->id); + if (err) + return err; + + return len; +} + +ssize_t dumcnt_count_min_read(struct simple_counter_device *counter, + struct simple_counter_count *count, void *priv, char *buf) +{ + struct dumcnt *const drv_data = counter->priv; + + return scnprintf(buf, PAGE_SIZE, "%ld\n", drv_data->min[count->id]); +} + +ssize_t dumcnt_count_min_write(struct simple_counter_device *counter, + struct simple_counter_count *count, void *priv, const char *buf, + size_t len) +{ + int err; + struct dumcnt *const drv_data = counter->priv; + + err = kstrtol(buf, 0, drv_data->min + count->id); + if (err) + return err; + + return len; +} + +static const struct simple_counter_count_ext dumcnt_count_ext[] = { + { + .name = "max", + .read = dumcnt_count_max_read, + .write = dumcnt_count_max_write + }, + { + .name = "min", + .read = dumcnt_count_min_read, + .write = dumcnt_count_min_write + } +}; + +#define DUMCNT_COUNT(_id, _cntname, _signame) { \ + .id = _id, \ + .name = _cntname, \ + .signal = { \ + .id = _id, \ + .name = _signame \ + }, \ + .ext = dumcnt_count_ext, \ + .num_ext = ARRAY_SIZE(dumcnt_count_ext) \ +} + +static const struct simple_counter_count dumcnt_counts[] = { + DUMCNT_COUNT(0, "Count A", "Signal A"), + DUMCNT_COUNT(1, "Count B", "Signal B") +}; + +static int dumcnt_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct simple_counter_count *counts; + struct dumcnt *dumcnt; + + counts = devm_kmemdup(dev, dumcnt_counts, sizeof(dumcnt_counts), + GFP_KERNEL); + if (!counts) + return -ENOMEM; + + dumcnt = devm_kzalloc(dev, sizeof(*dumcnt), GFP_KERNEL); + if (!dumcnt) + return -ENOMEM; + + dumcnt->counter.name = dev_name(dev); + dumcnt->counter.parent = dev; + dumcnt->counter.signal_read = dumcnt_signal_read; + dumcnt->counter.signal_write = dumcnt_signal_write; + dumcnt->counter.count_read = dumcnt_count_read; + dumcnt->counter.count_write = dumcnt_count_write; + dumcnt->counter.function_get = dumcnt_function_get; + dumcnt->counter.function_set = dumcnt_function_set; + dumcnt->counter.action_get = dumcnt_action_get; + dumcnt->counter.action_set = dumcnt_action_set; + dumcnt->counter.counts = counts; + dumcnt->counter.num_counts = ARRAY_SIZE(dumcnt_counts); + dumcnt->counter.priv = dumcnt; + + return devm_simple_counter_register(dev, &dumcnt->counter); +} + +static struct platform_device *dumcnt_device; + +static struct platform_driver dumcnt_driver = { + .driver = { + .name = "dummy-counter" + } +}; + +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");