diff options
author | Martin Sperl <kernel@martin.sperl.org> | 2015-12-14 10:20:20 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-02-09 14:32:07 -0500 |
commit | d9f1212272818420fcde611a940c1ad611a8b785 (patch) | |
tree | 0799bc9b7950503331bfbb384fbda85a427b9eeb | |
parent | 523baf5a0609690cb742b3662b7ccac0ea0b2ef2 (diff) |
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>
-rw-r--r-- | drivers/spi/spi.c | 111 | ||||
-rw-r--r-- | include/linux/spi/spi.h | 15 |
2 files changed, 126 insertions, 0 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 2ec8e66a8098..34e3741504f9 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c | |||
@@ -144,6 +144,8 @@ SPI_STATISTICS_TRANSFER_BYTES_HISTO(14, "16384-32767"); | |||
144 | SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535"); | 144 | SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535"); |
145 | SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+"); | 145 | SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+"); |
146 | 146 | ||
147 | SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu"); | ||
148 | |||
147 | static struct attribute *spi_dev_attrs[] = { | 149 | static struct attribute *spi_dev_attrs[] = { |
148 | &dev_attr_modalias.attr, | 150 | &dev_attr_modalias.attr, |
149 | NULL, | 151 | NULL, |
@@ -181,6 +183,7 @@ static struct attribute *spi_device_statistics_attrs[] = { | |||
181 | &dev_attr_spi_device_transfer_bytes_histo14.attr, | 183 | &dev_attr_spi_device_transfer_bytes_histo14.attr, |
182 | &dev_attr_spi_device_transfer_bytes_histo15.attr, | 184 | &dev_attr_spi_device_transfer_bytes_histo15.attr, |
183 | &dev_attr_spi_device_transfer_bytes_histo16.attr, | 185 | &dev_attr_spi_device_transfer_bytes_histo16.attr, |
186 | &dev_attr_spi_device_transfers_split_maxsize.attr, | ||
184 | NULL, | 187 | NULL, |
185 | }; | 188 | }; |
186 | 189 | ||
@@ -223,6 +226,7 @@ static struct attribute *spi_master_statistics_attrs[] = { | |||
223 | &dev_attr_spi_master_transfer_bytes_histo14.attr, | 226 | &dev_attr_spi_master_transfer_bytes_histo14.attr, |
224 | &dev_attr_spi_master_transfer_bytes_histo15.attr, | 227 | &dev_attr_spi_master_transfer_bytes_histo15.attr, |
225 | &dev_attr_spi_master_transfer_bytes_histo16.attr, | 228 | &dev_attr_spi_master_transfer_bytes_histo16.attr, |
229 | &dev_attr_spi_master_transfers_split_maxsize.attr, | ||
226 | NULL, | 230 | NULL, |
227 | }; | 231 | }; |
228 | 232 | ||
@@ -2237,6 +2241,113 @@ struct spi_replaced_transfers *spi_replace_transfers( | |||
2237 | } | 2241 | } |
2238 | EXPORT_SYMBOL_GPL(spi_replace_transfers); | 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 | /* Core methods for SPI master protocol drivers. Some of the | 2353 | /* Core methods for SPI master protocol drivers. Some of the |
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index d71385756fee..3c02b4d06268 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h | |||
@@ -53,6 +53,10 @@ extern struct bus_type spi_bus_type; | |||
53 | * | 53 | * |
54 | * @transfer_bytes_histo: | 54 | * @transfer_bytes_histo: |
55 | * transfer bytes histogramm | 55 | * transfer bytes histogramm |
56 | * | ||
57 | * @transfers_split_maxsize: | ||
58 | * number of transfers that have been split because of | ||
59 | * maxsize limit | ||
56 | */ | 60 | */ |
57 | struct spi_statistics { | 61 | struct spi_statistics { |
58 | spinlock_t lock; /* lock for the whole structure */ | 62 | spinlock_t lock; /* lock for the whole structure */ |
@@ -72,6 +76,8 @@ struct spi_statistics { | |||
72 | 76 | ||
73 | #define SPI_STATISTICS_HISTO_SIZE 17 | 77 | #define SPI_STATISTICS_HISTO_SIZE 17 |
74 | unsigned long transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE]; | 78 | unsigned long transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE]; |
79 | |||
80 | unsigned long transfers_split_maxsize; | ||
75 | }; | 81 | }; |
76 | 82 | ||
77 | void spi_statistics_add_transfer_stats(struct spi_statistics *stats, | 83 | void spi_statistics_add_transfer_stats(struct spi_statistics *stats, |
@@ -935,6 +941,15 @@ extern struct spi_replaced_transfers *spi_replace_transfers( | |||
935 | 941 | ||
936 | /*---------------------------------------------------------------------------*/ | 942 | /*---------------------------------------------------------------------------*/ |
937 | 943 | ||
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 | |||
938 | /* All these synchronous SPI transfer routines are utilities layered | 953 | /* All these synchronous SPI transfer routines are utilities layered |
939 | * over the core async transfer primitive. Here, "synchronous" means | 954 | * over the core async transfer primitive. Here, "synchronous" means |
940 | * they will sleep uninterruptibly until the async transfer completes. | 955 | * they will sleep uninterruptibly until the async transfer completes. |