mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-04 17:44:14 +08:00
spi: core: add spi_split_transfers_maxsize
Add spi_split_transfers_maxsize method that splits spi_transfers transparently into multiple transfers that are below the given max-size. This makes use of the spi_res framework via spi_replace_transfers to allocate/free the extra transfers as well as reverting back the changes applied while processing the spi_message. Signed-off-by: Martin Sperl <kernel@martin.sperl.org> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
523baf5a06
commit
d9f1212272
@ -144,6 +144,8 @@ SPI_STATISTICS_TRANSFER_BYTES_HISTO(14, "16384-32767");
|
|||||||
SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535");
|
SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535");
|
||||||
SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+");
|
SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+");
|
||||||
|
|
||||||
|
SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu");
|
||||||
|
|
||||||
static struct attribute *spi_dev_attrs[] = {
|
static struct attribute *spi_dev_attrs[] = {
|
||||||
&dev_attr_modalias.attr,
|
&dev_attr_modalias.attr,
|
||||||
NULL,
|
NULL,
|
||||||
@ -181,6 +183,7 @@ static struct attribute *spi_device_statistics_attrs[] = {
|
|||||||
&dev_attr_spi_device_transfer_bytes_histo14.attr,
|
&dev_attr_spi_device_transfer_bytes_histo14.attr,
|
||||||
&dev_attr_spi_device_transfer_bytes_histo15.attr,
|
&dev_attr_spi_device_transfer_bytes_histo15.attr,
|
||||||
&dev_attr_spi_device_transfer_bytes_histo16.attr,
|
&dev_attr_spi_device_transfer_bytes_histo16.attr,
|
||||||
|
&dev_attr_spi_device_transfers_split_maxsize.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -223,6 +226,7 @@ static struct attribute *spi_master_statistics_attrs[] = {
|
|||||||
&dev_attr_spi_master_transfer_bytes_histo14.attr,
|
&dev_attr_spi_master_transfer_bytes_histo14.attr,
|
||||||
&dev_attr_spi_master_transfer_bytes_histo15.attr,
|
&dev_attr_spi_master_transfer_bytes_histo15.attr,
|
||||||
&dev_attr_spi_master_transfer_bytes_histo16.attr,
|
&dev_attr_spi_master_transfer_bytes_histo16.attr,
|
||||||
|
&dev_attr_spi_master_transfers_split_maxsize.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2237,6 +2241,113 @@ struct spi_replaced_transfers *spi_replace_transfers(
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(spi_replace_transfers);
|
EXPORT_SYMBOL_GPL(spi_replace_transfers);
|
||||||
|
|
||||||
|
int __spi_split_transfer_maxsize(struct spi_master *master,
|
||||||
|
struct spi_message *msg,
|
||||||
|
struct spi_transfer **xferp,
|
||||||
|
size_t maxsize,
|
||||||
|
gfp_t gfp)
|
||||||
|
{
|
||||||
|
struct spi_transfer *xfer = *xferp, *xfers;
|
||||||
|
struct spi_replaced_transfers *srt;
|
||||||
|
size_t offset;
|
||||||
|
size_t count, i;
|
||||||
|
|
||||||
|
/* warn once about this fact that we are splitting a transfer */
|
||||||
|
dev_warn_once(&msg->spi->dev,
|
||||||
|
"spi_transfer of length %i exceed max length of %i - needed to split transfers\n",
|
||||||
|
xfer->len, maxsize);
|
||||||
|
|
||||||
|
/* calculate how many we have to replace */
|
||||||
|
count = DIV_ROUND_UP(xfer->len, maxsize);
|
||||||
|
|
||||||
|
/* create replacement */
|
||||||
|
srt = spi_replace_transfers(msg, xfer, 1, count, NULL, 0, gfp);
|
||||||
|
if (!srt)
|
||||||
|
return -ENOMEM;
|
||||||
|
xfers = srt->inserted_transfers;
|
||||||
|
|
||||||
|
/* now handle each of those newly inserted spi_transfers
|
||||||
|
* note that the replacements spi_transfers all are preset
|
||||||
|
* to the same values as *xferp, so tx_buf, rx_buf and len
|
||||||
|
* are all identical (as well as most others)
|
||||||
|
* so we just have to fix up len and the pointers.
|
||||||
|
*
|
||||||
|
* this also includes support for the depreciated
|
||||||
|
* spi_message.is_dma_mapped interface
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* the first transfer just needs the length modified, so we
|
||||||
|
* run it outside the loop
|
||||||
|
*/
|
||||||
|
xfers[0].len = min(maxsize, xfer[0].len);
|
||||||
|
|
||||||
|
/* all the others need rx_buf/tx_buf also set */
|
||||||
|
for (i = 1, offset = maxsize; i < count; offset += maxsize, i++) {
|
||||||
|
/* update rx_buf, tx_buf and dma */
|
||||||
|
if (xfers[i].rx_buf)
|
||||||
|
xfers[i].rx_buf += offset;
|
||||||
|
if (xfers[i].rx_dma)
|
||||||
|
xfers[i].rx_dma += offset;
|
||||||
|
if (xfers[i].tx_buf)
|
||||||
|
xfers[i].tx_buf += offset;
|
||||||
|
if (xfers[i].tx_dma)
|
||||||
|
xfers[i].tx_dma += offset;
|
||||||
|
|
||||||
|
/* update length */
|
||||||
|
xfers[i].len = min(maxsize, xfers[i].len - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we set up xferp to the last entry we have inserted,
|
||||||
|
* so that we skip those already split transfers
|
||||||
|
*/
|
||||||
|
*xferp = &xfers[count - 1];
|
||||||
|
|
||||||
|
/* increment statistics counters */
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
|
||||||
|
transfers_split_maxsize);
|
||||||
|
SPI_STATISTICS_INCREMENT_FIELD(&msg->spi->statistics,
|
||||||
|
transfers_split_maxsize);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spi_split_tranfers_maxsize - split spi transfers into multiple transfers
|
||||||
|
* when an individual transfer exceeds a
|
||||||
|
* certain size
|
||||||
|
* @master: the @spi_master for this transfer
|
||||||
|
* @message: the @spi_message to transform
|
||||||
|
* @max_size: the maximum when to apply this
|
||||||
|
*
|
||||||
|
* Return: status of transformation
|
||||||
|
*/
|
||||||
|
int spi_split_transfers_maxsize(struct spi_master *master,
|
||||||
|
struct spi_message *msg,
|
||||||
|
size_t maxsize,
|
||||||
|
gfp_t gfp)
|
||||||
|
{
|
||||||
|
struct spi_transfer *xfer;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* iterate over the transfer_list,
|
||||||
|
* but note that xfer is advanced to the last transfer inserted
|
||||||
|
* to avoid checking sizes again unnecessarily (also xfer does
|
||||||
|
* potentiall belong to a different list by the time the
|
||||||
|
* replacement has happened
|
||||||
|
*/
|
||||||
|
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||||
|
if (xfer->len > maxsize) {
|
||||||
|
ret = __spi_split_transfer_maxsize(
|
||||||
|
master, msg, &xfer, maxsize, gfp);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(spi_split_transfers_maxsize);
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* Core methods for SPI master protocol drivers. Some of the
|
/* Core methods for SPI master protocol drivers. Some of the
|
||||||
|
@ -53,6 +53,10 @@ extern struct bus_type spi_bus_type;
|
|||||||
*
|
*
|
||||||
* @transfer_bytes_histo:
|
* @transfer_bytes_histo:
|
||||||
* transfer bytes histogramm
|
* transfer bytes histogramm
|
||||||
|
*
|
||||||
|
* @transfers_split_maxsize:
|
||||||
|
* number of transfers that have been split because of
|
||||||
|
* maxsize limit
|
||||||
*/
|
*/
|
||||||
struct spi_statistics {
|
struct spi_statistics {
|
||||||
spinlock_t lock; /* lock for the whole structure */
|
spinlock_t lock; /* lock for the whole structure */
|
||||||
@ -72,6 +76,8 @@ struct spi_statistics {
|
|||||||
|
|
||||||
#define SPI_STATISTICS_HISTO_SIZE 17
|
#define SPI_STATISTICS_HISTO_SIZE 17
|
||||||
unsigned long transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE];
|
unsigned long transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE];
|
||||||
|
|
||||||
|
unsigned long transfers_split_maxsize;
|
||||||
};
|
};
|
||||||
|
|
||||||
void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
|
void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
|
||||||
@ -935,6 +941,15 @@ extern struct spi_replaced_transfers *spi_replace_transfers(
|
|||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* SPI transfer transformation methods */
|
||||||
|
|
||||||
|
extern int spi_split_transfers_maxsize(struct spi_master *master,
|
||||||
|
struct spi_message *msg,
|
||||||
|
size_t maxsize,
|
||||||
|
gfp_t gfp);
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/* All these synchronous SPI transfer routines are utilities layered
|
/* All these synchronous SPI transfer routines are utilities layered
|
||||||
* over the core async transfer primitive. Here, "synchronous" means
|
* over the core async transfer primitive. Here, "synchronous" means
|
||||||
* they will sleep uninterruptibly until the async transfer completes.
|
* they will sleep uninterruptibly until the async transfer completes.
|
||||||
|
Loading…
Reference in New Issue
Block a user