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: Martin 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: Mark Brown <broonie@kernel.org>
This commit is contained in:
Martin Sperl 2019-04-23 20:15:13 +00:00 committed by Mark Brown
parent ff245d90eb
commit 154f7da56f
No known key found for this signature in database
GPG key ID: 24D68B725D5487D0

View file

@ -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);