@@ -144,6 +144,8 @@ SPI_STATISTICS_TRANSFER_BYTES_HISTO(14, "16384-32767");
144
144
SPI_STATISTICS_TRANSFER_BYTES_HISTO (15 , "32768-65535" );
145
145
SPI_STATISTICS_TRANSFER_BYTES_HISTO (16 , "65536+" );
146
146
147
+ SPI_STATISTICS_SHOW (transfers_split_maxsize , "%lu" );
148
+
147
149
static struct attribute * spi_dev_attrs [] = {
148
150
& dev_attr_modalias .attr ,
149
151
NULL ,
@@ -181,6 +183,7 @@ static struct attribute *spi_device_statistics_attrs[] = {
181
183
& dev_attr_spi_device_transfer_bytes_histo14 .attr ,
182
184
& dev_attr_spi_device_transfer_bytes_histo15 .attr ,
183
185
& dev_attr_spi_device_transfer_bytes_histo16 .attr ,
186
+ & dev_attr_spi_device_transfers_split_maxsize .attr ,
184
187
NULL ,
185
188
};
186
189
@@ -223,6 +226,7 @@ static struct attribute *spi_master_statistics_attrs[] = {
223
226
& dev_attr_spi_master_transfer_bytes_histo14 .attr ,
224
227
& dev_attr_spi_master_transfer_bytes_histo15 .attr ,
225
228
& dev_attr_spi_master_transfer_bytes_histo16 .attr ,
229
+ & dev_attr_spi_master_transfers_split_maxsize .attr ,
226
230
NULL ,
227
231
};
228
232
@@ -2237,6 +2241,113 @@ struct spi_replaced_transfers *spi_replace_transfers(
2237
2241
}
2238
2242
EXPORT_SYMBOL_GPL (spi_replace_transfers );
2239
2243
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
+
2240
2351
/*-------------------------------------------------------------------------*/
2241
2352
2242
2353
/* Core methods for SPI master protocol drivers. Some of the
0 commit comments