spi: Fixes for v6.11

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.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmajpbAACgkQJNaLcl1U
 h9BK3gf6A/EoyfNTbY7eqnGtghg6PXqBzWHkfHtdqxMNvlf2YBYPu0Zj4i/1Tqde
 3UcDCZnKdYBIH3GX0wzRlTa/OUZmfZ6i5oyUAfqRCYFj8w9AydTLrfm6d9MizgWf
 ZrRcBLRxMqUvlADH4odWqXBo8+Eyx2A2n98bVhIEDPieJDaRtlUz1oym5AKmInP+
 AuiF43N8HSoumv/zmwMZWit9TugUnyqGeeKiUBoW5SDDsvCUGy/2hCkyoJDGZDCg
 NpEllllMNz1Weggt3e3q52mEV9bfjoyefRg/z9/TEIEyuYFWCqZ1ilbrKKaVVXF8
 JKicvFtVDErk5iVx7a5090JXcgh15Q==
 =CfDJ
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2024-07-27 12:29:10 -07:00
commit ab11658f26
3 changed files with 114 additions and 81 deletions

View File

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

View File

@ -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;
u32 control = mchp_corespi_read(spi, REG_CONTROL);
mchp_corespi_disable(spi);
control = mchp_corespi_read(spi, REG_CONTROL);
control |= mask;
mchp_corespi_write(spi, REG_CONTROL, 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;
u32 control = mchp_corespi_read(spi, REG_CONTROL);
mchp_corespi_disable(spi);
control = mchp_corespi_read(spi, REG_CONTROL);
control &= ~mask;
mchp_corespi_write(spi, REG_CONTROL, 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;

View File

@ -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 },