Merge branch 'Scatter-gather-SPI-for-SJA1105-DSA'

Vladimir Oltean says:

====================
Scatter/gather SPI for SJA1105 DSA

This is a small series that reduces the stack memory usage for the
sja1105 driver.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-10-15 13:16:57 -04:00
commit 1c9dc2b529
2 changed files with 85 additions and 99 deletions

View File

@ -121,7 +121,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv);
/* From sja1105_spi.c */
int sja1105_xfer_buf(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 reg_addr,
void *packed_buf, size_t size_bytes);
u8 *buf, size_t len);
int sja1105_xfer_u32(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 reg_addr, u32 *value);
int sja1105_xfer_u64(const struct sja1105_private *priv,

View File

@ -10,38 +10,12 @@
#define SJA1105_SIZE_RESET_CMD 4
#define SJA1105_SIZE_SPI_MSG_HEADER 4
#define SJA1105_SIZE_SPI_MSG_MAXLEN (64 * 4)
#define SJA1105_SIZE_SPI_TRANSFER_MAX \
(SJA1105_SIZE_SPI_MSG_HEADER + SJA1105_SIZE_SPI_MSG_MAXLEN)
static int sja1105_spi_transfer(const struct sja1105_private *priv,
const void *tx, void *rx, int size)
{
struct spi_device *spi = priv->spidev;
struct spi_transfer transfer = {
.tx_buf = tx,
.rx_buf = rx,
.len = size,
};
struct spi_message msg;
int rc;
if (size > SJA1105_SIZE_SPI_TRANSFER_MAX) {
dev_err(&spi->dev, "SPI message (%d) longer than max of %d\n",
size, SJA1105_SIZE_SPI_TRANSFER_MAX);
return -EMSGSIZE;
}
spi_message_init(&msg);
spi_message_add_tail(&transfer, &msg);
rc = spi_sync(spi, &msg);
if (rc < 0) {
dev_err(&spi->dev, "SPI transfer failed: %d\n", rc);
return rc;
}
return rc;
}
struct sja1105_chunk {
u8 *buf;
size_t len;
u64 reg_addr;
};
static void
sja1105_spi_message_pack(void *buf, const struct sja1105_spi_message *msg)
@ -55,49 +29,98 @@ sja1105_spi_message_pack(void *buf, const struct sja1105_spi_message *msg)
sja1105_pack(buf, &msg->address, 24, 4, size);
}
#define sja1105_hdr_xfer(xfers, chunk) \
((xfers) + 2 * (chunk))
#define sja1105_chunk_xfer(xfers, chunk) \
((xfers) + 2 * (chunk) + 1)
#define sja1105_hdr_buf(hdr_bufs, chunk) \
((hdr_bufs) + (chunk) * SJA1105_SIZE_SPI_MSG_HEADER)
/* If @rw is:
* - SPI_WRITE: creates and sends an SPI write message at absolute
* address reg_addr, taking size_bytes from *packed_buf
* address reg_addr, taking @len bytes from *buf
* - SPI_READ: creates and sends an SPI read message from absolute
* address reg_addr, writing size_bytes into *packed_buf
*
* This function should only be called if it is priorly known that
* @size_bytes is smaller than SIZE_SPI_MSG_MAXLEN. Larger packed buffers
* are chunked in smaller pieces by sja1105_xfer_long_buf below.
* address reg_addr, writing @len bytes into *buf
*/
int sja1105_xfer_buf(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 reg_addr,
void *packed_buf, size_t size_bytes)
u8 *buf, size_t len)
{
u8 tx_buf[SJA1105_SIZE_SPI_TRANSFER_MAX] = {0};
u8 rx_buf[SJA1105_SIZE_SPI_TRANSFER_MAX] = {0};
const int msg_len = size_bytes + SJA1105_SIZE_SPI_MSG_HEADER;
struct sja1105_spi_message msg = {0};
int rc;
struct sja1105_chunk chunk = {
.len = min_t(size_t, len, SJA1105_SIZE_SPI_MSG_MAXLEN),
.reg_addr = reg_addr,
.buf = buf,
};
struct spi_device *spi = priv->spidev;
struct spi_transfer *xfers;
int num_chunks;
int rc, i = 0;
u8 *hdr_bufs;
if (msg_len > SJA1105_SIZE_SPI_TRANSFER_MAX)
return -ERANGE;
num_chunks = DIV_ROUND_UP(len, SJA1105_SIZE_SPI_MSG_MAXLEN);
msg.access = rw;
msg.address = reg_addr;
if (rw == SPI_READ)
msg.read_count = size_bytes / 4;
/* One transfer for each message header, one for each message
* payload (chunk).
*/
xfers = kcalloc(2 * num_chunks, sizeof(struct spi_transfer),
GFP_KERNEL);
if (!xfers)
return -ENOMEM;
sja1105_spi_message_pack(tx_buf, &msg);
/* Packed buffers for the num_chunks SPI message headers,
* stored as a contiguous array
*/
hdr_bufs = kcalloc(num_chunks, SJA1105_SIZE_SPI_MSG_HEADER,
GFP_KERNEL);
if (!hdr_bufs) {
kfree(xfers);
return -ENOMEM;
}
if (rw == SPI_WRITE)
memcpy(tx_buf + SJA1105_SIZE_SPI_MSG_HEADER,
packed_buf, size_bytes);
for (i = 0; i < num_chunks; i++) {
struct spi_transfer *chunk_xfer = sja1105_chunk_xfer(xfers, i);
struct spi_transfer *hdr_xfer = sja1105_hdr_xfer(xfers, i);
u8 *hdr_buf = sja1105_hdr_buf(hdr_bufs, i);
struct sja1105_spi_message msg;
rc = sja1105_spi_transfer(priv, tx_buf, rx_buf, msg_len);
/* Populate the transfer's header buffer */
msg.address = chunk.reg_addr;
msg.access = rw;
if (rw == SPI_READ)
msg.read_count = chunk.len / 4;
else
/* Ignored */
msg.read_count = 0;
sja1105_spi_message_pack(hdr_buf, &msg);
hdr_xfer->tx_buf = hdr_buf;
hdr_xfer->len = SJA1105_SIZE_SPI_MSG_HEADER;
/* Populate the transfer's data buffer */
if (rw == SPI_READ)
chunk_xfer->rx_buf = chunk.buf;
else
chunk_xfer->tx_buf = chunk.buf;
chunk_xfer->len = chunk.len;
/* Calculate next chunk */
chunk.buf += chunk.len;
chunk.reg_addr += chunk.len / 4;
chunk.len = min_t(size_t, (ptrdiff_t)(buf + len - chunk.buf),
SJA1105_SIZE_SPI_MSG_MAXLEN);
/* De-assert the chip select after each chunk. */
if (chunk.len)
chunk_xfer->cs_change = 1;
}
rc = spi_sync_transfer(spi, xfers, 2 * num_chunks);
if (rc < 0)
return rc;
dev_err(&spi->dev, "SPI transfer failed: %d\n", rc);
if (rw == SPI_READ)
memcpy(packed_buf, rx_buf + SJA1105_SIZE_SPI_MSG_HEADER,
size_bytes);
kfree(hdr_bufs);
kfree(xfers);
return 0;
return rc;
}
/* If @rw is:
@ -152,43 +175,6 @@ int sja1105_xfer_u32(const struct sja1105_private *priv,
return rc;
}
/* Should be used if a @packed_buf larger than SJA1105_SIZE_SPI_MSG_MAXLEN
* must be sent/received. Splitting the buffer into chunks and assembling
* those into SPI messages is done automatically by this function.
*/
static int sja1105_xfer_long_buf(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 base_addr,
void *packed_buf, u64 buf_len)
{
struct chunk {
void *buf_ptr;
int len;
u64 spi_address;
} chunk;
int distance_to_end;
int rc;
/* Initialize chunk */
chunk.buf_ptr = packed_buf;
chunk.spi_address = base_addr;
chunk.len = min_t(int, buf_len, SJA1105_SIZE_SPI_MSG_MAXLEN);
while (chunk.len) {
rc = sja1105_xfer_buf(priv, rw, chunk.spi_address,
chunk.buf_ptr, chunk.len);
if (rc < 0)
return rc;
chunk.buf_ptr += chunk.len;
chunk.spi_address += chunk.len / 4;
distance_to_end = (uintptr_t)(packed_buf + buf_len -
chunk.buf_ptr);
chunk.len = min(distance_to_end, SJA1105_SIZE_SPI_MSG_MAXLEN);
}
return 0;
}
/* Back-ported structure from UM11040 Table 112.
* Reset control register (addr. 100440h)
* In the SJA1105 E/T, only warm_rst and cold_rst are
@ -451,8 +437,8 @@ int sja1105_static_config_upload(struct sja1105_private *priv)
/* Wait for the switch to come out of reset */
usleep_range(1000, 5000);
/* Upload the static config to the device */
rc = sja1105_xfer_long_buf(priv, SPI_WRITE, regs->config,
config_buf, buf_len);
rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->config,
config_buf, buf_len);
if (rc < 0) {
dev_err(dev, "Failed to upload config, retrying...\n");
continue;