Commit 1c9dc2b5 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents f58a887e 08839c06
...@@ -121,7 +121,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv); ...@@ -121,7 +121,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv);
/* From sja1105_spi.c */ /* From sja1105_spi.c */
int sja1105_xfer_buf(const struct sja1105_private *priv, int sja1105_xfer_buf(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 reg_addr, 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, int sja1105_xfer_u32(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 reg_addr, u32 *value); sja1105_spi_rw_mode_t rw, u64 reg_addr, u32 *value);
int sja1105_xfer_u64(const struct sja1105_private *priv, int sja1105_xfer_u64(const struct sja1105_private *priv,
......
...@@ -10,38 +10,12 @@ ...@@ -10,38 +10,12 @@
#define SJA1105_SIZE_RESET_CMD 4 #define SJA1105_SIZE_RESET_CMD 4
#define SJA1105_SIZE_SPI_MSG_HEADER 4 #define SJA1105_SIZE_SPI_MSG_HEADER 4
#define SJA1105_SIZE_SPI_MSG_MAXLEN (64 * 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, struct sja1105_chunk {
const void *tx, void *rx, int size) u8 *buf;
{ size_t len;
struct spi_device *spi = priv->spidev; u64 reg_addr;
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;
}
static void static void
sja1105_spi_message_pack(void *buf, const struct sja1105_spi_message *msg) 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) ...@@ -55,49 +29,98 @@ sja1105_spi_message_pack(void *buf, const struct sja1105_spi_message *msg)
sja1105_pack(buf, &msg->address, 24, 4, size); 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: /* If @rw is:
* - SPI_WRITE: creates and sends an SPI write message at absolute * - 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 * - SPI_READ: creates and sends an SPI read message from absolute
* address reg_addr, writing size_bytes into *packed_buf * address reg_addr, writing @len bytes into *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.
*/ */
int sja1105_xfer_buf(const struct sja1105_private *priv, int sja1105_xfer_buf(const struct sja1105_private *priv,
sja1105_spi_rw_mode_t rw, u64 reg_addr, 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}; struct sja1105_chunk chunk = {
u8 rx_buf[SJA1105_SIZE_SPI_TRANSFER_MAX] = {0}; .len = min_t(size_t, len, SJA1105_SIZE_SPI_MSG_MAXLEN),
const int msg_len = size_bytes + SJA1105_SIZE_SPI_MSG_HEADER; .reg_addr = reg_addr,
struct sja1105_spi_message msg = {0}; .buf = buf,
int rc; };
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) num_chunks = DIV_ROUND_UP(len, SJA1105_SIZE_SPI_MSG_MAXLEN);
return -ERANGE;
msg.access = rw; /* One transfer for each message header, one for each message
msg.address = reg_addr; * payload (chunk).
if (rw == SPI_READ) */
msg.read_count = size_bytes / 4; 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) for (i = 0; i < num_chunks; i++) {
memcpy(tx_buf + SJA1105_SIZE_SPI_MSG_HEADER, struct spi_transfer *chunk_xfer = sja1105_chunk_xfer(xfers, i);
packed_buf, size_bytes); struct spi_transfer *hdr_xfer = sja1105_hdr_xfer(xfers, i);
u8 *hdr_buf = sja1105_hdr_buf(hdr_bufs, i);
struct sja1105_spi_message msg;
/* 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 = sja1105_spi_transfer(priv, tx_buf, rx_buf, msg_len); rc = spi_sync_transfer(spi, xfers, 2 * num_chunks);
if (rc < 0) if (rc < 0)
return rc; dev_err(&spi->dev, "SPI transfer failed: %d\n", rc);
if (rw == SPI_READ) kfree(hdr_bufs);
memcpy(packed_buf, rx_buf + SJA1105_SIZE_SPI_MSG_HEADER, kfree(xfers);
size_bytes);
return 0; return rc;
} }
/* If @rw is: /* If @rw is:
...@@ -152,43 +175,6 @@ int sja1105_xfer_u32(const struct sja1105_private *priv, ...@@ -152,43 +175,6 @@ int sja1105_xfer_u32(const struct sja1105_private *priv,
return rc; 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. /* Back-ported structure from UM11040 Table 112.
* Reset control register (addr. 100440h) * Reset control register (addr. 100440h)
* In the SJA1105 E/T, only warm_rst and cold_rst are * 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) ...@@ -451,8 +437,8 @@ int sja1105_static_config_upload(struct sja1105_private *priv)
/* Wait for the switch to come out of reset */ /* Wait for the switch to come out of reset */
usleep_range(1000, 5000); usleep_range(1000, 5000);
/* Upload the static config to the device */ /* Upload the static config to the device */
rc = sja1105_xfer_long_buf(priv, SPI_WRITE, regs->config, rc = sja1105_xfer_buf(priv, SPI_WRITE, regs->config,
config_buf, buf_len); config_buf, buf_len);
if (rc < 0) { if (rc < 0) {
dev_err(dev, "Failed to upload config, retrying...\n"); dev_err(dev, "Failed to upload config, retrying...\n");
continue; continue;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment