Skip to content

Commit d9f1212

Browse files
msperlbroonie
authored andcommitted
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>
1 parent 523baf5 commit d9f1212

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

drivers/spi/spi.c

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,8 @@ SPI_STATISTICS_TRANSFER_BYTES_HISTO(14, "16384-32767");
144144
SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535");
145145
SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+");
146146

147+
SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu");
148+
147149
static struct attribute *spi_dev_attrs[] = {
148150
&dev_attr_modalias.attr,
149151
NULL,
@@ -181,6 +183,7 @@ static struct attribute *spi_device_statistics_attrs[] = {
181183
&dev_attr_spi_device_transfer_bytes_histo14.attr,
182184
&dev_attr_spi_device_transfer_bytes_histo15.attr,
183185
&dev_attr_spi_device_transfer_bytes_histo16.attr,
186+
&dev_attr_spi_device_transfers_split_maxsize.attr,
184187
NULL,
185188
};
186189

@@ -223,6 +226,7 @@ static struct attribute *spi_master_statistics_attrs[] = {
223226
&dev_attr_spi_master_transfer_bytes_histo14.attr,
224227
&dev_attr_spi_master_transfer_bytes_histo15.attr,
225228
&dev_attr_spi_master_transfer_bytes_histo16.attr,
229+
&dev_attr_spi_master_transfers_split_maxsize.attr,
226230
NULL,
227231
};
228232

@@ -2237,6 +2241,113 @@ struct spi_replaced_transfers *spi_replace_transfers(
22372241
}
22382242
EXPORT_SYMBOL_GPL(spi_replace_transfers);
22392243

2244+
int __spi_split_transfer_maxsize(struct spi_master *master,
2245+
struct spi_message *msg,
2246+
struct spi_transfer **xferp,
2247+
size_t maxsize,
2248+
gfp_t gfp)
2249+
{
2250+
struct spi_transfer *xfer = *xferp, *xfers;
2251+
struct spi_replaced_transfers *srt;
2252+
size_t offset;
2253+
size_t count, i;
2254+
2255+
/* warn once about this fact that we are splitting a transfer */
2256+
dev_warn_once(&msg->spi->dev,
2257+
"spi_transfer of length %i exceed max length of %i - needed to split transfers\n",
2258+
xfer->len, maxsize);
2259+
2260+
/* calculate how many we have to replace */
2261+
count = DIV_ROUND_UP(xfer->len, maxsize);
2262+
2263+
/* create replacement */
2264+
srt = spi_replace_transfers(msg, xfer, 1, count, NULL, 0, gfp);
2265+
if (!srt)
2266+
return -ENOMEM;
2267+
xfers = srt->inserted_transfers;
2268+
2269+
/* now handle each of those newly inserted spi_transfers
2270+
* note that the replacements spi_transfers all are preset
2271+
* to the same values as *xferp, so tx_buf, rx_buf and len
2272+
* are all identical (as well as most others)
2273+
* so we just have to fix up len and the pointers.
2274+
*
2275+
* this also includes support for the depreciated
2276+
* spi_message.is_dma_mapped interface
2277+
*/
2278+
2279+
/* the first transfer just needs the length modified, so we
2280+
* run it outside the loop
2281+
*/
2282+
xfers[0].len = min(maxsize, xfer[0].len);
2283+
2284+
/* all the others need rx_buf/tx_buf also set */
2285+
for (i = 1, offset = maxsize; i < count; offset += maxsize, i++) {
2286+
/* update rx_buf, tx_buf and dma */
2287+
if (xfers[i].rx_buf)
2288+
xfers[i].rx_buf += offset;
2289+
if (xfers[i].rx_dma)
2290+
xfers[i].rx_dma += offset;
2291+
if (xfers[i].tx_buf)
2292+
xfers[i].tx_buf += offset;
2293+
if (xfers[i].tx_dma)
2294+
xfers[i].tx_dma += offset;
2295+
2296+
/* update length */
2297+
xfers[i].len = min(maxsize, xfers[i].len - offset);
2298+
}
2299+
2300+
/* we set up xferp to the last entry we have inserted,
2301+
* so that we skip those already split transfers
2302+
*/
2303+
*xferp = &xfers[count - 1];
2304+
2305+
/* increment statistics counters */
2306+
SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
2307+
transfers_split_maxsize);
2308+
SPI_STATISTICS_INCREMENT_FIELD(&msg->spi->statistics,
2309+
transfers_split_maxsize);
2310+
2311+
return 0;
2312+
}
2313+
2314+
/**
2315+
* spi_split_tranfers_maxsize - split spi transfers into multiple transfers
2316+
* when an individual transfer exceeds a
2317+
* certain size
2318+
* @master: the @spi_master for this transfer
2319+
* @message: the @spi_message to transform
2320+
* @max_size: the maximum when to apply this
2321+
*
2322+
* Return: status of transformation
2323+
*/
2324+
int spi_split_transfers_maxsize(struct spi_master *master,
2325+
struct spi_message *msg,
2326+
size_t maxsize,
2327+
gfp_t gfp)
2328+
{
2329+
struct spi_transfer *xfer;
2330+
int ret;
2331+
2332+
/* iterate over the transfer_list,
2333+
* but note that xfer is advanced to the last transfer inserted
2334+
* to avoid checking sizes again unnecessarily (also xfer does
2335+
* potentiall belong to a different list by the time the
2336+
* replacement has happened
2337+
*/
2338+
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
2339+
if (xfer->len > maxsize) {
2340+
ret = __spi_split_transfer_maxsize(
2341+
master, msg, &xfer, maxsize, gfp);
2342+
if (ret)
2343+
return ret;
2344+
}
2345+
}
2346+
2347+
return 0;
2348+
}
2349+
EXPORT_SYMBOL_GPL(spi_split_transfers_maxsize);
2350+
22402351
/*-------------------------------------------------------------------------*/
22412352

22422353
/* Core methods for SPI master protocol drivers. Some of the

include/linux/spi/spi.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ extern struct bus_type spi_bus_type;
5353
*
5454
* @transfer_bytes_histo:
5555
* transfer bytes histogramm
56+
*
57+
* @transfers_split_maxsize:
58+
* number of transfers that have been split because of
59+
* maxsize limit
5660
*/
5761
struct spi_statistics {
5862
spinlock_t lock; /* lock for the whole structure */
@@ -72,6 +76,8 @@ struct spi_statistics {
7276

7377
#define SPI_STATISTICS_HISTO_SIZE 17
7478
unsigned long transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE];
79+
80+
unsigned long transfers_split_maxsize;
7581
};
7682

7783
void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
@@ -935,6 +941,15 @@ extern struct spi_replaced_transfers *spi_replace_transfers(
935941

936942
/*---------------------------------------------------------------------------*/
937943

944+
/* SPI transfer transformation methods */
945+
946+
extern int spi_split_transfers_maxsize(struct spi_master *master,
947+
struct spi_message *msg,
948+
size_t maxsize,
949+
gfp_t gfp);
950+
951+
/*---------------------------------------------------------------------------*/
952+
938953
/* All these synchronous SPI transfer routines are utilities layered
939954
* over the core async transfer primitive. Here, "synchronous" means
940955
* they will sleep uninterruptibly until the async transfer completes.

0 commit comments

Comments
 (0)