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:
commit
1c9dc2b529
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue