From patchwork Tue Mar 1 14:43:16 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksij Rempel X-Patchwork-Id: 8465651 Return-Path: X-Original-To: patchwork-linux-spi@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 74A20C0553 for ; Tue, 1 Mar 2016 14:46:10 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 538C820154 for ; Tue, 1 Mar 2016 14:46:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 49A93202AE for ; Tue, 1 Mar 2016 14:46:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754220AbcCAOp5 (ORCPT ); Tue, 1 Mar 2016 09:45:57 -0500 Received: from mout.gmx.net ([212.227.15.18]:64081 "EHLO mout.gmx.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754451AbcCAOp4 (ORCPT ); Tue, 1 Mar 2016 09:45:56 -0500 Received: from localhost.localdomain ([80.187.99.114]) by mail.gmx.com (mrgmx002) with ESMTPSA (Nemesis) id 0LfSeH-1a7jJD44Jl-00p3Xu; Tue, 01 Mar 2016 15:45:50 +0100 From: Oleksij Rempel To: fixed-term.Oleksij.Rempel@de.bosch.com, geert@linux-m68k.org, dirk.behme@de.bosch.com, broonie@kernel.org, linux-spi@vger.kernel.org, devicetree@vger.kernel.org, robh+dt@kernel.org Cc: Oleksij Rempel Subject: [PATCH 2/6] spi: add add flow control test driver Date: Tue, 1 Mar 2016 15:43:16 +0100 Message-Id: <1456843400-20696-2-git-send-email-linux@rempel-privat.de> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1456843400-20696-1-git-send-email-linux@rempel-privat.de> References: <56D448E1.6090006@de.bosch.com> <1456843400-20696-1-git-send-email-linux@rempel-privat.de> X-Provags-ID: V03:K0:MOGBD7+XxY1r4PTR//tw4obUkHAiAMcZnJ5qfJoVKh5d3+XNb+I Bm6nr0jcG4xKmcAc4oQBKUY3h18Df7p/ZO69n3WTzvsBB70Vr0uofZV5zPzzfh0YNRL6+vm HNWzrpS8H8ddlhVo6mTqm/WtBezZs19zfHoKczILU/yS736WkJZdkOhnZDS0mgindMVlc/X 6GI5zaADR/csvC9jHBmbQ== X-UI-Out-Filterresults: notjunk:1; V01:K0:miw4bl0zI7s=:FU1hqRVx/PCy84cHZpzTux XaIbyud1zJEXlBO+6UwqOl5ipf6yeHq8ITS0wnZlOIYHSfakKEfxuENXfBQ84GlODYbZVwkd8 mErz0AOnMq5FXDqU+9nZz30dWN77X8D8tXW//dMrAHmx+Lu8Xw30yONoxchp/9i1Xg/C8uWny ouz83K0AH5wtZFurYsdJW0wOmMKh81Lv4j2SdddI4eAWIjW9NAZZN9B7rPXeL398vyUlKpBwr t4B8Xj6A6S9vSvr0twXihaRCRCMeaspXEpxYDJtoIqfyc+wRoOlH3+FlxvIyrkzRLHTMcwTxR d5mTyPLcGW/YQJGK3MY/oZcbi8NlPc4uthco5nzI1kU/p/ziAI0zeAt6H9eesMiAk6Xzxb0G4 B8HPAUkxS+4XMrzTZOJEilxgDC6LeKJGeT656pk8FiL0qoEkJ/2QRq/wE+pC7mL5hfrW/VWNn dJSPJJF2i5BoWPYb5HKK0lTw4M7tu+E8Wd1fDZTAM3oIqPFz6/kJMgQ4y+FQ9R/P4MLn3g6qc eNy+GNY/7D3eUhRgPy+oRsJ5oi2HYKgsbPu0BYYu8SqAkPXy8k5c2uBLCD+6s306LLNISaTCc v0Jl9d18zCNDG71fx4cDIgfYY74nsfgTNOQQPrVS/AznBsKs5bJfsizsCUBQf0NYG+8rT9q9H Gi4Aag2U2iQxHjwSCrOjLZKYHwVAqkZHovBemhgDHWo0Q8U1HFuEsvboao3kcfl0z++ITSvXm BreMIlXXaCfXjtzXrb5tCSF1mCFCvoJldlNar4rpqsvFEzLgXJhoH4bOQ8WXLvaUDenzB8rG6 HPuHW5z Sender: linux-spi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-spi@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This testdriver can be used to test flow control functionality by using some gpio pins to emulate slave device. Signed-off-by: Oleksij Rempel --- drivers/spi/Kconfig | 12 ++ drivers/spi/Makefile | 1 + drivers/spi/spi_fc_test.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+) create mode 100644 drivers/spi/spi_fc_test.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 7706416..fb96295 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -681,6 +681,18 @@ config SPI_DW_MMIO # comment "SPI Protocol Masters" +config SPI_FC_TEST + tristate "Test for SPI flow control functionality" + depends on SPI + default n + help + This option enables test module for flow control SPI + extensions. For testing use debugfs interface with count + of packet wich should be used for testing. For example: + echo 100000 > /sys/kernel/debug/spi_fc_test/spi0/test_fc_request + + If unsure, say N. + config SPI_SPIDEV tristate "User mode SPI device driver support" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8991ffc..282224a 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -37,6 +37,7 @@ spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o obj-$(CONFIG_SPI_EFM32) += spi-efm32.o obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o obj-$(CONFIG_SPI_FALCON) += spi-falcon.o +obj-$(CONFIG_SPI_FC_TEST) += spi_fc_test.o obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o diff --git a/drivers/spi/spi_fc_test.c b/drivers/spi/spi_fc_test.c new file mode 100644 index 0000000..65bff99 --- /dev/null +++ b/drivers/spi/spi_fc_test.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) Robert Bosch Car Multimedia GmbH + * Copyright (C) Oleksij Rempel + * + * Licensed under GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "spi_fc_test" + +static struct dentry *debugfs_root; +/* + * Some registers must be read back to modify. + * To save time we cache them here in memory + */ +struct spi_fc_test_priv { + struct mutex lock; /* protect from simultaneous accesses */ + u8 port_config; + struct spi_device *spi; + struct gpio_desc *test_cs_in; + struct gpio_desc *test_fc_out; + struct dentry *debugfs; + struct completion fc_complete; + atomic_t active_rq; +}; + +static int spi_fc_test_set(struct spi_fc_test_priv *priv) +{ + u8 buf; + + buf = 0xaa; + return spi_write(priv->spi, &buf, sizeof(buf)); +} + +static void spi_fc_test_request_cb(struct spi_device *spi) +{ + struct spi_fc_test_priv *priv = spi_get_drvdata(spi); + + complete(&priv->fc_complete); +} + +static ssize_t spi_fc_fops_request_write(struct file *file, const char + __user *user_buf, size_t count, loff_t *data) +{ + struct spi_fc_test_priv *priv = file_inode(file)->i_private ?: + PDE_DATA(file_inode(file)); + unsigned long timeout = msecs_to_jiffies(10); + u32 rounds; + int ret; + + if (kstrtouint_from_user(user_buf, count, 0, &rounds)) + return -EINVAL; + + mutex_lock(&priv->lock); + while (rounds > 0) { + atomic_set(&priv->active_rq, 1); + reinit_completion(&priv->fc_complete); + gpiod_set_value(priv->test_fc_out, true); + ret = wait_for_completion_io_timeout(&priv->fc_complete, + timeout); + if (!ret) { + dev_err(&priv->spi->dev, "Request timeout\n"); + goto exit; + } + ret = spi_fc_test_set(priv); + if (ret < 0) { + dev_err(&priv->spi->dev, "SPI transfer error\n"); + goto exit; + } + rounds--; + } +exit: + gpiod_set_value(priv->test_fc_out, false); + mutex_unlock(&priv->lock); + return count; +} + +const struct file_operations spi_fc_fops_request = { + .owner = THIS_MODULE, + .write = spi_fc_fops_request_write, +}; + +static ssize_t spi_fc_fops_ready_write(struct file *file, const char + __user *user_buf, size_t count, loff_t *data) +{ + struct spi_fc_test_priv *priv = file_inode(file)->i_private ?: + PDE_DATA(file_inode(file)); + u32 rounds; + int ret; + + if (kstrtouint_from_user(user_buf, count, 0, &rounds)) + return -EINVAL; + + mutex_lock(&priv->lock); + while (rounds > 0) { + ret = spi_fc_test_set(priv); + if (ret < 0) { + mutex_unlock(&priv->lock); + return ret; + } + rounds--; + } + mutex_unlock(&priv->lock); + + return count; +} + +const struct file_operations spi_fc_fops_ready = { + .owner = THIS_MODULE, + .write = spi_fc_fops_ready_write, +}; + +static irqreturn_t spi_fc_test_isr(int irq, void *dev_id) +{ + struct spi_fc_test_priv *priv = (struct spi_fc_test_priv *)dev_id; + int val; + + if (atomic_read(&priv->active_rq)) { + atomic_set(&priv->active_rq, 0); + return IRQ_HANDLED; + } + + val = gpiod_get_value(priv->test_cs_in); + gpiod_set_value(priv->test_fc_out, val); + + return IRQ_HANDLED; +} + +static int spi_fc_test_probe(struct spi_device *spi) +{ + struct spi_fc_test_priv *priv; + struct dentry *de; + int ret, cs_irq; + + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) + return ret; + + priv = devm_kzalloc(&spi->dev, sizeof(struct spi_fc_test_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mutex_init(&priv->lock); + init_completion(&priv->fc_complete); + + spi_set_drvdata(spi, priv); + + priv->spi = spi; + spi->master->rt = 1; + spi->request_cb = spi_fc_test_request_cb; + + ret = spi_fc_probe(spi); + if (ret) { + dev_err(&spi->dev, "filed to probe FC\n"); + return ret; + } + + priv->test_fc_out = devm_gpiod_get(&spi->dev, "test-fc-out", + GPIOD_OUT_HIGH); + if (IS_ERR(priv->test_fc_out)) { + ret = PTR_ERR(priv->test_fc_out); + dev_err(&spi->dev, "failed to request FC GPIO: %d\n", ret); + return ret; + } + + priv->test_cs_in = devm_gpiod_get(&spi->dev, "test-cs-in", GPIOD_IN); + if (IS_ERR(priv->test_cs_in)) { + ret = PTR_ERR(priv->test_cs_in); + dev_err(&spi->dev, "failed to request CS GPIO: %d\n", ret); + return ret; + } + + cs_irq = gpiod_to_irq(priv->test_cs_in); + if (cs_irq < 0) { + dev_err(&spi->dev, "failed to reques irq for GPIO\n"); + return -ENODEV; + } + + ret = devm_request_irq(&spi->dev, cs_irq, spi_fc_test_isr, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "test_cs_in", priv); + if (ret) { + dev_err(&spi->dev, "failed to request IRQ\n"); + return ret; + } + + priv->debugfs = debugfs_create_dir(dev_name(&spi->dev), debugfs_root); + de = debugfs_create_file_size("test_fc_ready", S_IRUGO, + priv->debugfs, priv, &spi_fc_fops_ready, + sizeof(u32)); + if (IS_ERR_OR_NULL(de)) { + dev_err(&spi->dev, "failed to create test_fc_ready\n"); + return -ENODEV; + } + + de = debugfs_create_file_size("test_fc_request", S_IRUGO, + priv->debugfs, priv, &spi_fc_fops_request, + sizeof(u32)); + if (IS_ERR_OR_NULL(de)) { + dev_err(&spi->dev, "failed to create test_fc_request\n"); + return -ENODEV; + } + + return ret; +} + +static int spi_fc_test_remove(struct spi_device *spi) +{ + struct spi_fc_test_priv *priv; + + priv = spi_get_drvdata(spi); + if (!priv) + return -ENODEV; + + mutex_destroy(&priv->lock); + + return 0; +} + +static const struct of_device_id spi_fc_test_gpio_match[] = { + { + .compatible = "spi-fc-test", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, spi_fc_test_gpio_match); + +static struct spi_driver spi_fc_test_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(spi_fc_test_gpio_match), + }, + .probe = spi_fc_test_probe, + .remove = spi_fc_test_remove, +}; + +static int __init spi_fc_test_init(void) +{ + debugfs_root = debugfs_create_dir(DRIVER_NAME, NULL); + if (IS_ERR_OR_NULL(debugfs_root)) { + pr_err("%s: Filed to create debufs entry.\n", DRIVER_NAME); + return -ENOMEM; + } + + return spi_register_driver(&spi_fc_test_driver); +} +subsys_initcall(spi_fc_test_init); + +static void __exit spi_fc_test_exit(void) +{ + debugfs_remove_recursive(debugfs_root); + spi_unregister_driver(&spi_fc_test_driver); +} +module_exit(spi_fc_test_exit); + +MODULE_AUTHOR("Oleksij Rempel "); +MODULE_LICENSE("GPL"); +