aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Sperl <kernel@martin.sperl.org>2015-12-14 10:20:19 -0500
committerMark Brown <broonie@kernel.org>2016-02-09 14:32:07 -0500
commit523baf5a0609690cb742b3662b7ccac0ea0b2ef2 (patch)
treee1b05ea7c4287cf1da5a72d4d0e446e3946165da
parentd780c3711d9df9bacd56b71cf23443b895a331ca (diff)
spi: core: add spi_replace_transfers method
Add the spi_replace_transfers method that can get used to replace some spi_transfers from a spi_message with other transfers. Signed-off-by: Martin Sperl <kernel@martin.sperl.org> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--drivers/spi/spi.c132
-rw-r--r--include/linux/spi/spi.h45
2 files changed, 177 insertions, 0 deletions
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 894ed0357dd7..2ec8e66a8098 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2107,6 +2107,138 @@ EXPORT_SYMBOL_GPL(spi_res_release);
2107 2107
2108/*-------------------------------------------------------------------------*/ 2108/*-------------------------------------------------------------------------*/
2109 2109
2110/* Core methods for spi_message alterations */
2111
2112static void __spi_replace_transfers_release(struct spi_master *master,
2113 struct spi_message *msg,
2114 void *res)
2115{
2116 struct spi_replaced_transfers *rxfer = res;
2117 size_t i;
2118
2119 /* call extra callback if requested */
2120 if (rxfer->release)
2121 rxfer->release(master, msg, res);
2122
2123 /* insert replaced transfers back into the message */
2124 list_splice(&rxfer->replaced_transfers, rxfer->replaced_after);
2125
2126 /* remove the formerly inserted entries */
2127 for (i = 0; i < rxfer->inserted; i++)
2128 list_del(&rxfer->inserted_transfers[i].transfer_list);
2129}
2130
2131/**
2132 * spi_replace_transfers - replace transfers with several transfers
2133 * and register change with spi_message.resources
2134 * @msg: the spi_message we work upon
2135 * @xfer_first: the first spi_transfer we want to replace
2136 * @remove: number of transfers to remove
2137 * @insert: the number of transfers we want to insert instead
2138 * @release: extra release code necessary in some circumstances
2139 * @extradatasize: extra data to allocate (with alignment guarantees
2140 * of struct @spi_transfer)
2141 *
2142 * Returns: pointer to @spi_replaced_transfers,
2143 * PTR_ERR(...) in case of errors.
2144 */
2145struct spi_replaced_transfers *spi_replace_transfers(
2146 struct spi_message *msg,
2147 struct spi_transfer *xfer_first,
2148 size_t remove,
2149 size_t insert,
2150 spi_replaced_release_t release,
2151 size_t extradatasize,
2152 gfp_t gfp)
2153{
2154 struct spi_replaced_transfers *rxfer;
2155 struct spi_transfer *xfer;
2156 size_t i;
2157
2158 /* allocate the structure using spi_res */
2159 rxfer = spi_res_alloc(msg->spi, __spi_replace_transfers_release,
2160 insert * sizeof(struct spi_transfer)
2161 + sizeof(struct spi_replaced_transfers)
2162 + extradatasize,
2163 gfp);
2164 if (!rxfer)
2165 return ERR_PTR(-ENOMEM);
2166
2167 /* the release code to invoke before running the generic release */
2168 rxfer->release = release;
2169
2170 /* assign extradata */
2171 if (extradatasize)
2172 rxfer->extradata =
2173 &rxfer->inserted_transfers[insert];
2174
2175 /* init the replaced_transfers list */
2176 INIT_LIST_HEAD(&rxfer->replaced_transfers);
2177
2178 /* assign the list_entry after which we should reinsert
2179 * the @replaced_transfers - it may be spi_message.messages!
2180 */
2181 rxfer->replaced_after = xfer_first->transfer_list.prev;
2182
2183 /* remove the requested number of transfers */
2184 for (i = 0; i < remove; i++) {
2185 /* if the entry after replaced_after it is msg->transfers
2186 * then we have been requested to remove more transfers
2187 * than are in the list
2188 */
2189 if (rxfer->replaced_after->next == &msg->transfers) {
2190 dev_err(&msg->spi->dev,
2191 "requested to remove more spi_transfers than are available\n");
2192 /* insert replaced transfers back into the message */
2193 list_splice(&rxfer->replaced_transfers,
2194 rxfer->replaced_after);
2195
2196 /* free the spi_replace_transfer structure */
2197 spi_res_free(rxfer);
2198
2199 /* and return with an error */
2200 return ERR_PTR(-EINVAL);
2201 }
2202
2203 /* remove the entry after replaced_after from list of
2204 * transfers and add it to list of replaced_transfers
2205 */
2206 list_move_tail(rxfer->replaced_after->next,
2207 &rxfer->replaced_transfers);
2208 }
2209
2210 /* create copy of the given xfer with identical settings
2211 * based on the first transfer to get removed
2212 */
2213 for (i = 0; i < insert; i++) {
2214 /* we need to run in reverse order */
2215 xfer = &rxfer->inserted_transfers[insert - 1 - i];
2216
2217 /* copy all spi_transfer data */
2218 memcpy(xfer, xfer_first, sizeof(*xfer));
2219
2220 /* add to list */
2221 list_add(&xfer->transfer_list, rxfer->replaced_after);
2222
2223 /* clear cs_change and delay_usecs for all but the last */
2224 if (i) {
2225 xfer->cs_change = false;
2226 xfer->delay_usecs = 0;
2227 }
2228 }
2229
2230 /* set up inserted */
2231 rxfer->inserted = insert;
2232
2233 /* and register it with spi_res/spi_message */
2234 spi_res_add(msg, rxfer);
2235
2236 return rxfer;
2237}
2238EXPORT_SYMBOL_GPL(spi_replace_transfers);
2239
2240/*-------------------------------------------------------------------------*/
2241
2110/* Core methods for SPI master protocol drivers. Some of the 2242/* Core methods for SPI master protocol drivers. Some of the
2111 * other core methods are currently defined as inline functions. 2243 * other core methods are currently defined as inline functions.
2112 */ 2244 */
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 38204b584dc5..d71385756fee 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -890,6 +890,51 @@ spi_max_transfer_size(struct spi_device *spi)
890 890
891/*---------------------------------------------------------------------------*/ 891/*---------------------------------------------------------------------------*/
892 892
893/* SPI transfer replacement methods which make use of spi_res */
894
895/**
896 * struct spi_replaced_transfers - structure describing the spi_transfer
897 * replacements that have occurred
898 * so that they can get reverted
899 * @release: some extra release code to get executed prior to
900 * relasing this structure
901 * @extradata: pointer to some extra data if requested or NULL
902 * @replaced_transfers: transfers that have been replaced and which need
903 * to get restored
904 * @replaced_after: the transfer after which the @replaced_transfers
905 * are to get re-inserted
906 * @inserted: number of transfers inserted
907 * @inserted_transfers: array of spi_transfers of array-size @inserted,
908 * that have been replacing replaced_transfers
909 *
910 * note: that @extradata will point to @inserted_transfers[@inserted]
911 * if some extra allocation is requested, so alignment will be the same
912 * as for spi_transfers
913 */
914struct spi_replaced_transfers;
915typedef void (*spi_replaced_release_t)(struct spi_master *master,
916 struct spi_message *msg,
917 struct spi_replaced_transfers *res);
918struct spi_replaced_transfers {
919 spi_replaced_release_t release;
920 void *extradata;
921 struct list_head replaced_transfers;
922 struct list_head *replaced_after;
923 size_t inserted;
924 struct spi_transfer inserted_transfers[];
925};
926
927extern struct spi_replaced_transfers *spi_replace_transfers(
928 struct spi_message *msg,
929 struct spi_transfer *xfer_first,
930 size_t remove,
931 size_t insert,
932 spi_replaced_release_t release,
933 size_t extradatasize,
934 gfp_t gfp);
935
936/*---------------------------------------------------------------------------*/
937
893/* All these synchronous SPI transfer routines are utilities layered 938/* All these synchronous SPI transfer routines are utilities layered
894 * over the core async transfer primitive. Here, "synchronous" means 939 * over the core async transfer primitive. Here, "synchronous" means
895 * they will sleep uninterruptibly until the async transfer completes. 940 * they will sleep uninterruptibly until the async transfer completes.