mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
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:
commit
ab11658f26
@ -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
|
||||
|
@ -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,8 +267,18 @@ 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;
|
||||
|
||||
mchp_corespi_write(corespi, REG_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);
|
||||
}
|
||||
|
||||
static int mchp_corespi_setup(struct spi_device *spi)
|
||||
@ -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;
|
||||
|
@ -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 },
|
||||
|
Loading…
Reference in New Issue
Block a user