Commit ab11658f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'spi-fix-v6.11-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi fixes from Mark Brown:
 "The bulk of this is a series of fixes for the microchip-core driver
  mostly originating from one of their customers, I also applied an
  additional patch adding support for controlling the word size which
  came along with it since it's still the merge window and clearly had a
  bunch of fairly thorough testing.

  We also have a fix for the compatible used to bind spidev to the
  BH2228FV"

* tag 'spi-fix-v6.11-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi:
  spi: spidev: add correct compatible for Rohm BH2228FV
  dt-bindings: trivial-devices: fix Rohm BH2228FV compatible string
  spi: microchip-core: add support for word sizes of 1 to 32 bits
  spi: microchip-core: ensure TX and RX FIFOs are empty at start of a transfer
  spi: microchip-core: fix init function not setting the master and motorola modes
  spi: microchip-core: only disable SPI controller when register value change requires it
  spi: microchip-core: defer asserting chip select until just before write to TX FIFO
  spi: microchip-core: fix the issues in the isr
parents 560e8050 fc28d1c1
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -328,7 +328,9 @@ properties:
          - renesas,hs3001
            # Renesas ISL29501 time-of-flight sensor
          - renesas,isl29501
            # Rohm DH2228FV
            # Rohm BH2228FV 8 channel DAC
          - rohm,bh2228fv
            # Rohm DH2228FV - This device does not exist, use rohm,bh2228fv instead.
          - rohm,dh2228fv
            # S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
          - samsung,24ad0xd1
+110 −80
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@

#define REG_CONTROL		(0x00)
#define REG_FRAME_SIZE		(0x04)
#define  FRAME_SIZE_MASK	GENMASK(5, 0)
#define REG_STATUS		(0x08)
#define REG_INT_CLEAR		(0x0c)
#define REG_RX_DATA		(0x10)
@@ -89,6 +90,9 @@
#define REG_RIS			(0x24)
#define REG_CONTROL2		(0x28)
#define REG_COMMAND		(0x2c)
#define  COMMAND_CLRFRAMECNT	BIT(4)
#define  COMMAND_TXFIFORST		BIT(3)
#define  COMMAND_RXFIFORST		BIT(2)
#define REG_PKTSIZE		(0x30)
#define REG_CMD_SIZE		(0x34)
#define REG_HWSTATUS		(0x38)
@@ -103,10 +107,11 @@ struct mchp_corespi {
	u8 *rx_buf;
	u32 clk_gen; /* divider for spi output clock generated by the controller */
	u32 clk_mode;
	u32 pending_slave_select;
	int irq;
	int tx_len;
	int rx_len;
	int pending;
	int n_bytes;
};

static inline u32 mchp_corespi_read(struct mchp_corespi *spi, unsigned int reg)
@@ -130,113 +135,126 @@ static inline void mchp_corespi_disable(struct mchp_corespi *spi)

static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi)
{
	u8 data;
	int fifo_max, i = 0;
	while (spi->rx_len >= spi->n_bytes && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)) {
		u32 data = mchp_corespi_read(spi, REG_RX_DATA);

	fifo_max = min(spi->rx_len, FIFO_DEPTH);
		spi->rx_len -= spi->n_bytes;

	while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)) {
		data = mchp_corespi_read(spi, REG_RX_DATA);
		if (!spi->rx_buf)
			continue;

		if (spi->rx_buf)
			*spi->rx_buf++ = data;
		i++;
		if (spi->n_bytes == 4)
			*((u32 *)spi->rx_buf) = data;
		else if (spi->n_bytes == 2)
			*((u16 *)spi->rx_buf) = data;
		else
			*spi->rx_buf = data;

		spi->rx_buf += spi->n_bytes;
	}
	spi->rx_len -= i;
	spi->pending -= i;
}

static void mchp_corespi_enable_ints(struct mchp_corespi *spi)
{
	u32 control, mask = INT_ENABLE_MASK;

	mchp_corespi_disable(spi);

	control = mchp_corespi_read(spi, REG_CONTROL);

	control |= mask;
	mchp_corespi_write(spi, REG_CONTROL, control);
	u32 control = mchp_corespi_read(spi, REG_CONTROL);

	control |= CONTROL_ENABLE;
	control |= INT_ENABLE_MASK;
	mchp_corespi_write(spi, REG_CONTROL, control);
}

static void mchp_corespi_disable_ints(struct mchp_corespi *spi)
{
	u32 control, mask = INT_ENABLE_MASK;

	mchp_corespi_disable(spi);

	control = mchp_corespi_read(spi, REG_CONTROL);
	control &= ~mask;
	mchp_corespi_write(spi, REG_CONTROL, control);
	u32 control = mchp_corespi_read(spi, REG_CONTROL);

	control |= CONTROL_ENABLE;
	control &= ~INT_ENABLE_MASK;
	mchp_corespi_write(spi, REG_CONTROL, control);
}

static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len)
{
	u32 control;
	u16 lenpart;
	u32 lenpart;
	u32 frames = mchp_corespi_read(spi, REG_FRAMESUP);

	/*
	 * Disable the SPI controller. Writes to transfer length have
	 * no effect when the controller is enabled.
	 * Writing to FRAMECNT in REG_CONTROL will reset the frame count, taking
	 * a shortcut requires an explicit clear.
	 */
	mchp_corespi_disable(spi);
	if (frames == len) {
		mchp_corespi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT);
		return;
	}

	/*
	 * The lower 16 bits of the frame count are stored in the control reg
	 * for legacy reasons, but the upper 16 written to a different register:
	 * FRAMESUP. While both the upper and lower bits can be *READ* from the
	 * FRAMESUP register, writing to the lower 16 bits is a NOP
	 * FRAMESUP register, writing to the lower 16 bits is (supposedly) a NOP.
	 *
	 * The driver used to disable the controller while modifying the frame
	 * count, and mask off the lower 16 bits of len while writing to
	 * FRAMES_UP. When the driver was changed to disable the controller as
	 * infrequently as possible, it was discovered that the logic of
	 * lenpart = len & 0xffff_0000
	 * write(REG_FRAMESUP, lenpart)
	 * would actually write zeros into the lower 16 bits on an mpfs250t-es,
	 * despite documentation stating these bits were read-only.
	 * Writing len unmasked into FRAMES_UP ensures those bits aren't zeroed
	 * on an mpfs250t-es and will be a NOP for the lower 16 bits on hardware
	 * that matches the documentation.
	 */
	lenpart = len & 0xffff;

	control = mchp_corespi_read(spi, REG_CONTROL);
	control &= ~CONTROL_FRAMECNT_MASK;
	control |= lenpart << CONTROL_FRAMECNT_SHIFT;
	mchp_corespi_write(spi, REG_CONTROL, control);

	lenpart = len & 0xffff0000;
	mchp_corespi_write(spi, REG_FRAMESUP, lenpart);

	control |= CONTROL_ENABLE;
	mchp_corespi_write(spi, REG_CONTROL, control);
	mchp_corespi_write(spi, REG_FRAMESUP, len);
}

static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi)
{
	u8 byte;
	int fifo_max, i = 0;

	fifo_max = min(spi->tx_len, FIFO_DEPTH);
	fifo_max = DIV_ROUND_UP(min(spi->tx_len, FIFO_DEPTH), spi->n_bytes);
	mchp_corespi_set_xfer_size(spi, fifo_max);

	while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) {
		byte = spi->tx_buf ? *spi->tx_buf++ : 0xaa;
		mchp_corespi_write(spi, REG_TX_DATA, byte);
		u32 word;

		if (spi->n_bytes == 4)
			word = spi->tx_buf ? *((u32 *)spi->tx_buf) : 0xaa;
		else if (spi->n_bytes == 2)
			word = spi->tx_buf ? *((u16 *)spi->tx_buf) : 0xaa;
		else
			word = spi->tx_buf ? *spi->tx_buf : 0xaa;

		mchp_corespi_write(spi, REG_TX_DATA, word);
		if (spi->tx_buf)
			spi->tx_buf += spi->n_bytes;
		i++;
	}

	spi->tx_len -= i;
	spi->pending += i;
	spi->tx_len -= i * spi->n_bytes;
}

static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt)
{
	u32 frame_size = mchp_corespi_read(spi, REG_FRAME_SIZE);
	u32 control;

	if ((frame_size & FRAME_SIZE_MASK) == bt)
		return;

	/*
	 * Disable the SPI controller. Writes to the frame size have
	 * no effect when the controller is enabled.
	 */
	mchp_corespi_disable(spi);
	control = mchp_corespi_read(spi, REG_CONTROL);
	control &= ~CONTROL_ENABLE;
	mchp_corespi_write(spi, REG_CONTROL, control);

	mchp_corespi_write(spi, REG_FRAME_SIZE, bt);

	control = mchp_corespi_read(spi, REG_CONTROL);
	control |= CONTROL_ENABLE;
	mchp_corespi_write(spi, REG_CONTROL, control);
}
@@ -249,7 +267,17 @@ static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
	reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
	reg &= ~BIT(spi_get_chipselect(spi, 0));
	reg |= !disable << spi_get_chipselect(spi, 0);
	corespi->pending_slave_select = reg;

	/*
	 * Only deassert chip select immediately. Writing to some registers
	 * requires the controller to be disabled, which results in the
	 * output pins being tristated and can cause the SCLK and MOSI lines
	 * to transition. Therefore asserting the chip select is deferred
	 * until just before writing to the TX FIFO, to ensure the device
	 * doesn't see any spurious clock transitions whilst CS is enabled.
	 */
	if (((spi->mode & SPI_CS_HIGH) == 0) == disable)
		mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
}

@@ -269,6 +297,7 @@ static int mchp_corespi_setup(struct spi_device *spi)
	if (spi->mode & SPI_CS_HIGH) {
		reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
		reg |= BIT(spi_get_chipselect(spi, 0));
		corespi->pending_slave_select = reg;
		mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
	}
	return 0;
@@ -279,17 +308,13 @@ static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *
	unsigned long clk_hz;
	u32 control = mchp_corespi_read(spi, REG_CONTROL);

	control |= CONTROL_MASTER;
	control &= ~CONTROL_ENABLE;
	mchp_corespi_write(spi, REG_CONTROL, control);

	control |= CONTROL_MASTER;
	control &= ~CONTROL_MODE_MASK;
	control |= MOTOROLA_MODE;

	mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE);

	/* max. possible spi clock rate is the apb clock rate */
	clk_hz = clk_get_rate(spi->clk);
	host->max_speed_hz = clk_hz;

	/*
	 * The controller must be configured so that it doesn't remove Chip
	 * Select until the entire message has been transferred, even if at
@@ -298,11 +323,16 @@ static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *
	 * BIGFIFO mode is also enabled, which sets the fifo depth to 32 frames
	 * for the 8 bit transfers that this driver uses.
	 */
	control = mchp_corespi_read(spi, REG_CONTROL);
	control |= CONTROL_SPS | CONTROL_BIGFIFO;

	mchp_corespi_write(spi, REG_CONTROL, control);

	mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE);

	/* max. possible spi clock rate is the apb clock rate */
	clk_hz = clk_get_rate(spi->clk);
	host->max_speed_hz = clk_hz;

	mchp_corespi_enable_ints(spi);

	/*
@@ -310,7 +340,8 @@ static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *
	 * select is relinquished to the hardware. SSELOUT is enabled too so we
	 * can deal with active high targets.
	 */
	mchp_corespi_write(spi, REG_SLAVE_SELECT, SSELOUT | SSEL_DIRECT);
	spi->pending_slave_select = SSELOUT | SSEL_DIRECT;
	mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);

	control = mchp_corespi_read(spi, REG_CONTROL);

@@ -324,8 +355,6 @@ static inline void mchp_corespi_set_clk_gen(struct mchp_corespi *spi)
{
	u32 control;

	mchp_corespi_disable(spi);

	control = mchp_corespi_read(spi, REG_CONTROL);
	if (spi->clk_mode)
		control |= CONTROL_CLKMODE;
@@ -334,12 +363,12 @@ static inline void mchp_corespi_set_clk_gen(struct mchp_corespi *spi)

	mchp_corespi_write(spi, REG_CLK_GEN, spi->clk_gen);
	mchp_corespi_write(spi, REG_CONTROL, control);
	mchp_corespi_write(spi, REG_CONTROL, control | CONTROL_ENABLE);
}

static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int mode)
{
	u32 control, mode_val;
	u32 mode_val;
	u32 control = mchp_corespi_read(spi, REG_CONTROL);

	switch (mode & SPI_MODE_X_MASK) {
	case SPI_MODE_0:
@@ -357,12 +386,13 @@ static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int
	}

	/*
	 * Disable the SPI controller. Writes to the frame size have
	 * Disable the SPI controller. Writes to the frame protocol have
	 * no effect when the controller is enabled.
	 */
	mchp_corespi_disable(spi);

	control = mchp_corespi_read(spi, REG_CONTROL);
	control &= ~CONTROL_ENABLE;
	mchp_corespi_write(spi, REG_CONTROL, control);

	control &= ~(SPI_MODE_X_MASK << MODE_X_MASK_SHIFT);
	control |= mode_val;

@@ -383,21 +413,18 @@ static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
	if (intfield == 0)
		return IRQ_NONE;

	if (intfield & INT_TXDONE) {
	if (intfield & INT_TXDONE)
		mchp_corespi_write(spi, REG_INT_CLEAR, INT_TXDONE);

	if (intfield & INT_RXRDY) {
		mchp_corespi_write(spi, REG_INT_CLEAR, INT_RXRDY);

		if (spi->rx_len)
			mchp_corespi_read_fifo(spi);

		if (spi->tx_len)
			mchp_corespi_write_fifo(spi);

		if (!spi->rx_len)
			finalise = true;
	}

	if (intfield & INT_RXRDY)
		mchp_corespi_write(spi, REG_INT_CLEAR, INT_RXRDY);
	if (!spi->rx_len && !spi->tx_len)
		finalise = true;

	if (intfield & INT_RX_CHANNEL_OVERFLOW) {
		mchp_corespi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW);
@@ -477,13 +504,17 @@ static int mchp_corespi_transfer_one(struct spi_controller *host,
	spi->rx_buf = xfer->rx_buf;
	spi->tx_len = xfer->len;
	spi->rx_len = xfer->len;
	spi->pending = 0;
	spi->n_bytes = roundup_pow_of_two(DIV_ROUND_UP(xfer->bits_per_word, BITS_PER_BYTE));

	mchp_corespi_set_xfer_size(spi, (spi->tx_len > FIFO_DEPTH)
				   ? FIFO_DEPTH : spi->tx_len);
	mchp_corespi_set_framesize(spi, xfer->bits_per_word);

	if (spi->tx_len)
	mchp_corespi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST);

	mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);

	while (spi->tx_len)
		mchp_corespi_write_fifo(spi);

	return 1;
}

@@ -493,7 +524,6 @@ static int mchp_corespi_prepare_message(struct spi_controller *host,
	struct spi_device *spi_dev = msg->spi;
	struct mchp_corespi *spi = spi_controller_get_devdata(host);

	mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE);
	mchp_corespi_set_mode(spi, spi_dev->mode);

	return 0;
@@ -521,7 +551,7 @@ static int mchp_corespi_probe(struct platform_device *pdev)
	host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
	host->use_gpio_descriptors = true;
	host->setup = mchp_corespi_setup;
	host->bits_per_word_mask = SPI_BPW_MASK(8);
	host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
	host->transfer_one = mchp_corespi_transfer_one;
	host->prepare_message = mchp_corespi_prepare_message;
	host->set_cs = mchp_corespi_set_cs;
+1 −0
Original line number Diff line number Diff line
@@ -734,6 +734,7 @@ static const struct of_device_id spidev_dt_ids[] = {
	{ .compatible = "lwn,bk4", .data = &spidev_of_check },
	{ .compatible = "menlo,m53cpld", .data = &spidev_of_check },
	{ .compatible = "micron,spi-authenta", .data = &spidev_of_check },
	{ .compatible = "rohm,bh2228fv", .data = &spidev_of_check },
	{ .compatible = "rohm,dh2228fv", .data = &spidev_of_check },
	{ .compatible = "semtech,sx1301", .data = &spidev_of_check },
	{ .compatible = "silabs,em3581", .data = &spidev_of_check },