diff options
Diffstat (limited to 'drivers/spi/spi-bcm2835aux.c')
| -rw-r--r-- | drivers/spi/spi-bcm2835aux.c | 205 | 
1 files changed, 148 insertions, 57 deletions
diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index f7e054848ca5..bbf87adb3ff8 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -21,6 +21,7 @@  #include <linux/clk.h>  #include <linux/completion.h> +#include <linux/debugfs.h>  #include <linux/delay.h>  #include <linux/err.h>  #include <linux/interrupt.h> @@ -36,6 +37,12 @@  #include <linux/spi/spi.h>  #include <linux/spinlock.h> +/* define polling limits */ +static unsigned int polling_limit_us = 30; +module_param(polling_limit_us, uint, 0664); +MODULE_PARM_DESC(polling_limit_us, +		 "time in us to run a transfer in polling mode - if zero no polling is used\n"); +  /*   * spi register defines   * @@ -88,10 +95,6 @@  #define BCM2835_AUX_SPI_STAT_BUSY	0x00000040  #define BCM2835_AUX_SPI_STAT_BITCOUNT	0x0000003F -/* timeout values */ -#define BCM2835_AUX_SPI_POLLING_LIMIT_US	30 -#define BCM2835_AUX_SPI_POLLING_JIFFIES		2 -  struct bcm2835aux_spi {  	void __iomem *regs;  	struct clk *clk; @@ -102,8 +105,53 @@ struct bcm2835aux_spi {  	int tx_len;  	int rx_len;  	int pending; + +	u64 count_transfer_polling; +	u64 count_transfer_irq; +	u64 count_transfer_irq_after_poll; + +	struct dentry *debugfs_dir;  }; +#if defined(CONFIG_DEBUG_FS) +static void bcm2835aux_debugfs_create(struct bcm2835aux_spi *bs, +				      const char *dname) +{ +	char name[64]; +	struct dentry *dir; + +	/* get full name */ +	snprintf(name, sizeof(name), "spi-bcm2835aux-%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_poll", 0444, dir, +			   &bs->count_transfer_irq_after_poll); +} + +static void bcm2835aux_debugfs_remove(struct bcm2835aux_spi *bs) +{ +	debugfs_remove_recursive(bs->debugfs_dir); +	bs->debugfs_dir = NULL; +} +#else +static void bcm2835aux_debugfs_create(struct bcm2835aux_spi *bs, +				      const char *dname) +{ +} + +static void bcm2835aux_debugfs_remove(struct bcm2835aux_spi *bs) +{ +} +#endif /* CONFIG_DEBUG_FS */ +  static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned reg)  {  	return readl(bs->regs + reg); @@ -123,9 +171,6 @@ static inline void bcm2835aux_rd_fifo(struct bcm2835aux_spi *bs)  	data = bcm2835aux_rd(bs, BCM2835_AUX_SPI_IO);  	if (bs->rx_buf) {  		switch (count) { -		case 4: -			*bs->rx_buf++ = (data >> 24) & 0xff; -			/* fallthrough */  		case 3:  			*bs->rx_buf++ = (data >> 16) & 0xff;  			/* fallthrough */ @@ -178,24 +223,14 @@ static void bcm2835aux_spi_reset_hw(struct bcm2835aux_spi *bs)  		      BCM2835_AUX_SPI_CNTL0_CLEARFIFO);  } -static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id) +static void bcm2835aux_spi_transfer_helper(struct bcm2835aux_spi *bs)  { -	struct spi_master *master = dev_id; -	struct bcm2835aux_spi *bs = spi_master_get_devdata(master); -	irqreturn_t ret = IRQ_NONE; - -	/* IRQ may be shared, so return if our interrupts are disabled */ -	if (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_CNTL1) & -	      (BCM2835_AUX_SPI_CNTL1_TXEMPTY | BCM2835_AUX_SPI_CNTL1_IDLE))) -		return ret; +	u32 stat = bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT);  	/* check if we have data to read */ -	while (bs->rx_len && -	       (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & -		  BCM2835_AUX_SPI_STAT_RX_EMPTY))) { +	for (; bs->rx_len && (stat & BCM2835_AUX_SPI_STAT_RX_LVL); +	     stat = bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT))  		bcm2835aux_rd_fifo(bs); -		ret = IRQ_HANDLED; -	}  	/* check if we have data to write */  	while (bs->tx_len && @@ -203,16 +238,21 @@ static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id)  	       (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) &  		  BCM2835_AUX_SPI_STAT_TX_FULL))) {  		bcm2835aux_wr_fifo(bs); -		ret = IRQ_HANDLED;  	} +} -	/* and check if we have reached "done" */ -	while (bs->rx_len && -	       (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & -		  BCM2835_AUX_SPI_STAT_BUSY))) { -		bcm2835aux_rd_fifo(bs); -		ret = IRQ_HANDLED; -	} +static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id) +{ +	struct spi_master *master = dev_id; +	struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + +	/* IRQ may be shared, so return if our interrupts are disabled */ +	if (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_CNTL1) & +	      (BCM2835_AUX_SPI_CNTL1_TXEMPTY | BCM2835_AUX_SPI_CNTL1_IDLE))) +		return IRQ_NONE; + +	/* do common fifo handling */ +	bcm2835aux_spi_transfer_helper(bs);  	if (!bs->tx_len) {  		/* disable tx fifo empty interrupt */ @@ -226,8 +266,7 @@ static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id)  		complete(&master->xfer_completion);  	} -	/* and return */ -	return ret; +	return IRQ_HANDLED;  }  static int __bcm2835aux_spi_transfer_one_irq(struct spi_master *master, @@ -251,6 +290,9 @@ static int bcm2835aux_spi_transfer_one_irq(struct spi_master *master,  {  	struct bcm2835aux_spi *bs = spi_master_get_devdata(master); +	/* update statistics */ +	bs->count_transfer_irq++; +  	/* fill in registers and fifos before enabling interrupts */  	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]);  	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); @@ -273,35 +315,22 @@ static int bcm2835aux_spi_transfer_one_poll(struct spi_master *master,  {  	struct bcm2835aux_spi *bs = spi_master_get_devdata(master);  	unsigned long timeout; -	u32 stat; + +	/* update statistics */ +	bs->count_transfer_polling++;  	/* configure spi */  	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]);  	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); -	/* set the timeout */ -	timeout = jiffies + BCM2835_AUX_SPI_POLLING_JIFFIES; +	/* set the timeout to at least 2 jiffies */ +	timeout = jiffies + 2 + HZ * polling_limit_us / 1000000;  	/* loop until finished the transfer */  	while (bs->rx_len) { -		/* read status */ -		stat = bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT); -		/* fill in tx fifo with remaining data */ -		if ((bs->tx_len) && (!(stat & BCM2835_AUX_SPI_STAT_TX_FULL))) { -			bcm2835aux_wr_fifo(bs); -			continue; -		} - -		/* read data from fifo for both cases */ -		if (!(stat & BCM2835_AUX_SPI_STAT_RX_EMPTY)) { -			bcm2835aux_rd_fifo(bs); -			continue; -		} -		if (!(stat & BCM2835_AUX_SPI_STAT_BUSY)) { -			bcm2835aux_rd_fifo(bs); -			continue; -		} +		/* do common fifo handling */ +		bcm2835aux_spi_transfer_helper(bs);  		/* there is still data pending to read check the timeout */  		if (bs->rx_len && time_after(jiffies, timeout)) { @@ -310,6 +339,7 @@ static int bcm2835aux_spi_transfer_one_poll(struct spi_master *master,  					    jiffies - timeout,  					    bs->tx_len, bs->rx_len);  			/* forward to interrupt handler */ +			bs->count_transfer_irq_after_poll++;  			return __bcm2835aux_spi_transfer_one_irq(master,  							       spi, tfr);  		} @@ -324,8 +354,8 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,  				       struct spi_transfer *tfr)  {  	struct bcm2835aux_spi *bs = spi_master_get_devdata(master); -	unsigned long spi_hz, clk_hz, speed; -	unsigned long spi_used_hz; +	unsigned long spi_hz, clk_hz, speed, spi_used_hz; +	unsigned long hz_per_byte, byte_limit;  	/* calculate the registers to handle  	 * @@ -369,14 +399,15 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,  	 * of Hz per byte per polling limit.  E.g., we can transfer 1 byte in  	 * 30 µs per 300,000 Hz of bus clock.  	 */ -#define HZ_PER_BYTE ((9 * 1000000) / BCM2835_AUX_SPI_POLLING_LIMIT_US) +	hz_per_byte = polling_limit_us ? (9 * 1000000) / polling_limit_us : 0; +	byte_limit = hz_per_byte ? spi_used_hz / hz_per_byte : 1; +  	/* run in polling mode for short transfers */ -	if (tfr->len < spi_used_hz / HZ_PER_BYTE) +	if (tfr->len < byte_limit)  		return bcm2835aux_spi_transfer_one_poll(master, spi, tfr);  	/* run in interrupt mode for all others */  	return bcm2835aux_spi_transfer_one_irq(master, spi, tfr); -#undef HZ_PER_BYTE  }  static int bcm2835aux_spi_prepare_message(struct spi_master *master, @@ -421,6 +452,50 @@ static void bcm2835aux_spi_handle_err(struct spi_master *master,  	bcm2835aux_spi_reset_hw(bs);  } +static int bcm2835aux_spi_setup(struct spi_device *spi) +{ +	int ret; + +	/* sanity check for native cs */ +	if (spi->mode & SPI_NO_CS) +		return 0; +	if (gpio_is_valid(spi->cs_gpio)) { +		/* with gpio-cs set the GPIO to the correct level +		 * and as output (in case the dt has the gpio not configured +		 * as output but native cs) +		 */ +		ret = gpio_direction_output(spi->cs_gpio, +					    (spi->mode & SPI_CS_HIGH) ? 0 : 1); +		if (ret) +			dev_err(&spi->dev, +				"could not set gpio %i as output: %i\n", +				spi->cs_gpio, ret); + +		return ret; +	} + +	/* for dt-backwards compatibility: only support native on CS0 +	 * known things not supported with broken native CS: +	 * * multiple chip-selects: cs0-cs2 are all +	 *     simultaniously asserted whenever there is a transfer +	 *     this even includes SPI_NO_CS +	 * * SPI_CS_HIGH: cs are always asserted low +	 * * cs_change: cs is deasserted after each spi_transfer +	 * * cs_delay_usec: cs is always deasserted one SCK cycle +	 *     after the last transfer +	 * probably more... +	 */ +	dev_warn(&spi->dev, +		 "Native CS is not supported - please configure cs-gpio in device-tree\n"); + +	if (spi->chip_select == 0) +		return 0; + +	dev_warn(&spi->dev, "Native CS is not working for cs > 0\n"); + +	return -EINVAL; +} +  static int bcm2835aux_spi_probe(struct platform_device *pdev)  {  	struct spi_master *master; @@ -438,7 +513,19 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, master);  	master->mode_bits = (SPI_CPOL | SPI_CS_HIGH | SPI_NO_CS);  	master->bits_per_word_mask = SPI_BPW_MASK(8); -	master->num_chipselect = -1; +	/* even though the driver never officially supported native CS +	 * allow a single native CS for legacy DT support purposes when +	 * no cs-gpio is configured. +	 * Known limitations for native cs are: +	 * * multiple chip-selects: cs0-cs2 are all simultaniously asserted +	 *     whenever there is a transfer -  this even includes SPI_NO_CS +	 * * SPI_CS_HIGH: is ignores - cs are always asserted low +	 * * cs_change: cs is deasserted after each spi_transfer +	 * * cs_delay_usec: cs is always deasserted one SCK cycle after +	 *     a spi_transfer +	 */ +	master->num_chipselect = 1; +	master->setup = bcm2835aux_spi_setup;  	master->transfer_one = bcm2835aux_spi_transfer_one;  	master->handle_err = bcm2835aux_spi_handle_err;  	master->prepare_message = bcm2835aux_spi_prepare_message; @@ -502,6 +589,8 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)  		goto out_clk_disable;  	} +	bcm2835aux_debugfs_create(bs, dev_name(&pdev->dev)); +  	return 0;  out_clk_disable: @@ -516,6 +605,8 @@ static int bcm2835aux_spi_remove(struct platform_device *pdev)  	struct spi_master *master = platform_get_drvdata(pdev);  	struct bcm2835aux_spi *bs = spi_master_get_devdata(master); +	bcm2835aux_debugfs_remove(bs); +  	bcm2835aux_spi_reset_hw(bs);  	/* disable the HW block by releasing the clock */  | 
