diff options
author | Wolfram Sang <wsa+renesas@sang-engineering.com> | 2020-12-29 14:36:35 +0100 |
---|---|---|
committer | Wolfram Sang <wsa@kernel.org> | 2020-12-30 13:11:50 +0100 |
commit | b4eb9fb765a99a599ac0d3440a8674a3ea10f659 (patch) | |
tree | add437efb6a78d6dd566720abde4042e1bfa8475 | |
parent | cd02a09ac5d060b2fab641295d2a270bb2e26296 (diff) | |
download | linux-renesas/topic/la-prototype-experimental.tar.gz |
WIP: gpio: add simple logic analyzer using pollingrenesas/topic/la-prototype-experimental
Not for upstream yet!
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
-rw-r--r-- | arch/arm64/boot/dts/renesas/salvator-common.dtsi | 4 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 3 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-la-poll.c | 206 |
4 files changed, 212 insertions, 2 deletions
diff --git a/arch/arm64/boot/dts/renesas/salvator-common.dtsi b/arch/arm64/boot/dts/renesas/salvator-common.dtsi index b8a7f37d289e67..a0000377de6266 100644 --- a/arch/arm64/boot/dts/renesas/salvator-common.dtsi +++ b/arch/arm64/boot/dts/renesas/salvator-common.dtsi @@ -110,7 +110,7 @@ }; logic-analyzer@pwm { - compatible = "logic-analyzer-irq"; + compatible = "logic-analyzer-poll"; probe-gpios = <&gpio6 21 GPIO_OPEN_DRAIN>, <&gpio6 4 GPIO_OPEN_DRAIN>; @@ -366,7 +366,7 @@ status = "okay"; - clock-frequency = <100000>; + clock-frequency = <400000>; ak4613: codec@10 { compatible = "asahi-kasei,ak4613"; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1f75218ecce6d0..f9d241bb7914fe 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1631,6 +1631,9 @@ config GPIO_AGGREGATOR config GPIO_LOGIC_ANALYZER_IRQ tristate "Simple GPIO logic analyzer IRQ" +config GPIO_LOGIC_ANALYZER_POLL + tristate "Simple GPIO logic analyzer POLL" + config GPIO_MOCKUP tristate "GPIO Testing Driver" select IRQ_SIM diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b40f5573580e7d..6ef2378a9855b2 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o obj-$(CONFIG_GPIO_REGMAP) += gpio-regmap.o obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o obj-$(CONFIG_GPIO_LOGIC_ANALYZER_IRQ) += gpio-la-irq.o +obj-$(CONFIG_GPIO_LOGIC_ANALYZER_POLL) += gpio-la-poll.o # directly supported by gpio-generic gpio-generic-$(CONFIG_GPIO_GENERIC) += gpio-mmio.o diff --git a/drivers/gpio/gpio-la-poll.c b/drivers/gpio/gpio-la-poll.c new file mode 100644 index 00000000000000..f5b2870bb29cf1 --- /dev/null +++ b/drivers/gpio/gpio-la-poll.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Simple logic analyzer using GPIOs + * + * Copyright (C) 2020 Wolfram Sang <wsa@sang-engineering.com> + * Copyright (C) 2020 Renesas Electronics Corporation + */ + +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/init.h> +#include <linux/ktime.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/sizes.h> +#include <linux/timekeeping.h> +#include <linux/vmalloc.h> + +#define GPIO_LA_DEFAULT_BUF_SIZE SZ_256K + +struct gpio_la_poll_priv { + unsigned long ndelay; + u32 buf_idx; + struct debugfs_blob_wrapper blob; + struct gpio_descs *descs; + struct dentry *debug_dir, *blob_dent; + struct debugfs_blob_wrapper meta; + unsigned long gpio_delay; +}; + +static struct dentry *gpio_la_poll_debug_dir; + +static int fops_capture_set(void *data, u64 val) +{ + struct gpio_la_poll_priv *priv = data; + u8 *la_buf = priv->blob.data; + unsigned long state = 0; + int ret; + + if (val) { + if (priv->blob_dent) { + debugfs_remove(priv->blob_dent); + priv->blob_dent = NULL; + } + + priv->buf_idx = 0; + + local_irq_disable(); + preempt_disable_notrace(); + while (priv->buf_idx < priv->blob.size && ret == 0) { + ret = gpiod_get_array_value(priv->descs->ndescs, priv->descs->desc, + priv->descs->info, &state); + la_buf[priv->buf_idx++] = state; + //la_buf[priv->buf_idx++] = gpiod_get_value(priv->descs->desc[0]); + ndelay(priv->ndelay); + } + preempt_enable_notrace(); + local_irq_enable(); + if (ret) + pr_err("%s: couldn't read GPIOs: %d\n", __func__, ret); + + priv->blob_dent = debugfs_create_blob("sample_data", 0400, priv->debug_dir, &priv->blob); + } + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_capture, NULL, fops_capture_set, "%llu\n"); + +static int fops_buf_size_get(void *data, u64 *val) +{ + struct gpio_la_poll_priv *priv = data; + + *val = priv->blob.size; + + return 0; +} + +static int fops_buf_size_set(void *data, u64 val) +{ + struct gpio_la_poll_priv *priv = data; + void *p; + + //FIXME: locking? + //FIXME: sanity checks on val + vfree(priv->blob.data); + p = vzalloc(val); + if (!p) + return -ENOMEM; + + priv->blob.data = p; + priv->blob.size = val; + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_buf_size, fops_buf_size_get, fops_buf_size_set, "%llu\n"); + +static int gpio_la_poll_probe(struct platform_device *pdev) +{ + struct gpio_la_poll_priv *priv; + struct device *dev = &pdev->dev; + char *meta = NULL; + unsigned long state; + ktime_t start_time, end_time; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + fops_buf_size_set(priv, GPIO_LA_DEFAULT_BUF_SIZE); + + priv->descs = devm_gpiod_get_array(dev, "probe", GPIOD_IN); + if (IS_ERR(priv->descs)) + return PTR_ERR(priv->descs); + + /* artificial limit to keep 1 byte per sample */ + if (priv->descs->ndescs > 8) + return -ERANGE; + + for (i = 0; i < priv->descs->ndescs; i++) { + const char *str, *old_meta; + + ret = of_property_read_string_index(pdev->dev.of_node, "probe-names", + i, &str); + if (ret == 0) { + gpiod_set_consumer_name(priv->descs->desc[i], str); + + old_meta = meta; + meta = devm_kasprintf(dev, GFP_KERNEL, "%sChannel %d: %s\n", + old_meta ?: "", i, str); + if (!meta) + return -ENOMEM; + + devm_kfree(dev, old_meta); + } + } + + priv->debug_dir = debugfs_create_dir(dev_name(dev), gpio_la_poll_debug_dir); + if (IS_ERR(priv->debug_dir)) + return PTR_ERR(priv->debug_dir); + + debugfs_create_file_unsafe("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size); + debugfs_create_file_unsafe("capture", 0600, priv->debug_dir, priv, &fops_capture); + debugfs_create_ulong("delay_ns_user", 0600, priv->debug_dir, &priv->ndelay); + + priv->meta.data = meta; + priv->meta.size = strlen(meta); + debugfs_create_blob("meta_data", 0400, priv->debug_dir, &priv->meta); + + local_irq_disable(); + preempt_disable_notrace(); + start_time = ktime_get(); + for (i = 0; i < 1024 && ret == 0; i++) + ret = gpiod_get_array_value(priv->descs->ndescs, priv->descs->desc, + priv->descs->info, &state); + end_time = ktime_get(); + preempt_enable_notrace(); + local_irq_enable(); + if (ret) { + dev_err(dev, "couldn't read GPIOs: %d\n", ret); + return ret; + } + + priv->gpio_delay = ktime_sub(end_time, start_time) / 1024; + debugfs_create_ulong("delay_ns_acquisition", 0400, priv->debug_dir, &priv->gpio_delay); + + return 0; +} + +static const struct of_device_id gpio_la_poll_of_match[] = { + { .compatible = "logic-analyzer-poll", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_la_poll_of_match); + +static struct platform_driver gpio_la_poll_device_driver = { + .probe = gpio_la_poll_probe, + //.remove = FIXME + .driver = { + .name = "logic-analyzer-poll", + .of_match_table = gpio_la_poll_of_match, + } +}; + +static int __init gpio_la_poll_init(void) +{ + gpio_la_poll_debug_dir = debugfs_create_dir("simple_logic_analyzer-poll", NULL); + if (IS_ERR(gpio_la_poll_debug_dir)) + return PTR_ERR(gpio_la_poll_debug_dir); + + return platform_driver_register(&gpio_la_poll_device_driver); +} +late_initcall(gpio_la_poll_init); + +static void __exit gpio_la_poll_exit(void) +{ + platform_driver_unregister(&gpio_la_poll_device_driver); + debugfs_remove_recursive(gpio_la_poll_debug_dir); +} +module_exit(gpio_la_poll_exit); + +MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>"); +MODULE_DESCRIPTION("Simple logic analyzer using GPIOs"); +MODULE_LICENSE("GPL v2"); |