Unverified Commit 154f7da5 authored by Martin Sperl's avatar Martin Sperl Committed by Mark Brown
Browse files

spi: bcm2835: add driver stats to debugfs



To estimate efficiency add statistics on transfer types
(polling, interrupt and dma) used to debugfs.

Signed-off-by: default avatarMartin Sperl <kernel@martin.sperl.org>

Changelog:
  V1 -> V2: applied feedback by Stefan Wahren
            reorganized patchset
	    added extra rational, descriptions
	    fixed compile issue when CONFIG_DEBUG_FS is unset
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent ff245d90
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@

#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
@@ -101,6 +102,15 @@ MODULE_PARM_DESC(polling_limit_us,
 *	length is not a multiple of 4 (to overcome hardware limitation)
 * @tx_spillover: whether @tx_prologue spills over to second TX sglist entry
 * @dma_pending: whether a DMA transfer is in progress
 * @debugfs_dir: the debugfs directory - neede to remove debugfs when
 *      unloading the module
 * @count_transfer_polling: count of how often polling mode is used
 * @count_transfer_irq: count of how often interrupt mode is used
 * @count_transfer_irq_after_polling: count of how often we fall back to
 *      interrupt mode after starting in polling mode.
 *      These are counted as well in @count_transfer_polling and
 *      @count_transfer_irq
 * @count_transfer_dma: count how often dma mode is used
 */
struct bcm2835_spi {
	void __iomem *regs;
@@ -115,8 +125,55 @@ struct bcm2835_spi {
	int rx_prologue;
	unsigned int tx_spillover;
	unsigned int dma_pending;

	struct dentry *debugfs_dir;
	u64 count_transfer_polling;
	u64 count_transfer_irq;
	u64 count_transfer_irq_after_polling;
	u64 count_transfer_dma;
};

#if defined(CONFIG_DEBUG_FS)
static void bcm2835_debugfs_create(struct bcm2835_spi *bs,
				   const char *dname)
{
	char name[64];
	struct dentry *dir;

	/* get full name */
	snprintf(name, sizeof(name), "spi-bcm2835-%s", dname);

	/* the base directory */
	dir = debugfs_create_dir(name, NULL);
	bs->debugfs_dir = dir;

	/* the counters */
	debugfs_create_u64("count_transfer_polling", 0444, dir,
			   &bs->count_transfer_polling);
	debugfs_create_u64("count_transfer_irq", 0444, dir,
			   &bs->count_transfer_irq);
	debugfs_create_u64("count_transfer_irq_after_polling", 0444, dir,
			   &bs->count_transfer_irq_after_polling);
	debugfs_create_u64("count_transfer_dma", 0444, dir,
			   &bs->count_transfer_dma);
}

static void bcm2835_debugfs_remove(struct bcm2835_spi *bs)
{
	debugfs_remove_recursive(bs->debugfs_dir);
	bs->debugfs_dir = NULL;
}
#else
static void bcm2835_debugfs_create(struct bcm2835_spi *bs,
				   const char *dname)
{
}

static void bcm2835_debugfs_remove(struct bcm2835_spi *bs)
{
}
#endif /* CONFIG_DEBUG_FS */

static inline u32 bcm2835_rd(struct bcm2835_spi *bs, unsigned reg)
{
	return readl(bs->regs + reg);
@@ -320,6 +377,9 @@ static int bcm2835_spi_transfer_one_irq(struct spi_master *master,
{
	struct bcm2835_spi *bs = spi_master_get_devdata(master);

	/* update usage statistics */
	bs->count_transfer_irq++;

	/*
	 * Enable HW block, but with interrupts still disabled.
	 * Otherwise the empty TX FIFO would immediately trigger an interrupt.
@@ -564,6 +624,9 @@ static int bcm2835_spi_transfer_one_dma(struct spi_master *master,
	struct bcm2835_spi *bs = spi_master_get_devdata(master);
	int ret;

	/* update usage statistics */
	bs->count_transfer_dma++;

	/*
	 * Transfer first few bytes without DMA if length of first TX or RX
	 * sglist entry is not a multiple of 4 bytes (hardware limitation).
@@ -706,6 +769,9 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
	struct bcm2835_spi *bs = spi_master_get_devdata(master);
	unsigned long timeout;

	/* update usage statistics */
	bs->count_transfer_polling++;

	/* enable HW block without interrupts */
	bcm2835_wr(bs, BCM2835_SPI_CS, cs | BCM2835_SPI_CS_TA);

@@ -735,6 +801,10 @@ static int bcm2835_spi_transfer_one_poll(struct spi_master *master,
					    jiffies - timeout,
					    bs->tx_len, bs->rx_len);
			/* fall back to interrupt mode */

			/* update usage statistics */
			bs->count_transfer_irq_after_polling++;

			return bcm2835_spi_transfer_one_irq(master, spi,
							    tfr, cs, false);
		}
@@ -982,6 +1052,8 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
		goto out_clk_disable;
	}

	bcm2835_debugfs_create(bs, dev_name(&pdev->dev));

	return 0;

out_clk_disable:
@@ -996,6 +1068,8 @@ static int bcm2835_spi_remove(struct platform_device *pdev)
	struct spi_master *master = platform_get_drvdata(pdev);
	struct bcm2835_spi *bs = spi_master_get_devdata(master);

	bcm2835_debugfs_remove(bs);

	/* Clear FIFOs, and disable the HW block */
	bcm2835_wr(bs, BCM2835_SPI_CS,
		   BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX);