Hi Mark! Here the new version of the patch that now: * applies against topic/core (and also got compiled) * has the bus-driver-specific stuff left out (but comments where this would go) * Driver hints are now only in spi_transfer * the optimize_hint bitfield is now a field of things that may change (as discussed)
See also a comment in code about the possible "generic" use of optimize: In principle this code could also generically set up DMA mapping - especially for platforms that do not have to do a bounce-buffer that they need to set up and copy to/from before/after DMA. Also just allocating a bounce buffer just once may also be possible optimization to reduce overhead. This would make it easier to avoid the "unloved" dma interface (rx_dma/tx_dma) for driver developers - they would just have to call spi_message_optimize and stop caring about DMA-able addresses - it would be taken care of... We could also make flags: #define SPI_OPTIMIZE_ALLOCATE_RXBUFFER (1<<6) #define SPI_OPTIMIZE_ALLOCATE_TXBUFFER (1<<7) which would allocate the rx/tx buffers for the driver with the positive side-effect that there is no more messing with dma_masks and such and would avoid the requirement of mapping/unmapping before/after each DMA transaction. (would need to happen in the spi_validate function to be generic enough and may require some capability hints from the dma framework of what it can support). Martin Signed-Off-By: Martin Sperl <[email protected]> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index e40b236..6d1d773 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1680,6 +1680,53 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message) return 0; } +/** + * spi_message_optimize - analysis of the spi message + * optimizing it for later use + * @spi: device with which data will be exchanged + * @message: describes the data transfers, including completion + * callback and optimization hints + * Context: can sleep + * + * This call optimizes the message for later efficient use. + * this may mean preparing DMA pattern for the bus in question. + * + * Between the optimize and unoptimize call the structure + * of the message may not get changed in any way + * unless explicitly stated in optimize_hints in the + * spi_transfer structure, which states explicitly which parts + * may change, so that the driver may make optimizations + * in handling these messages. + * + * For example: this may set up DMA mapping early without + * the driver having to provide rx_dma/tx_dma itself. + */ + +int spi_message_optimize(struct spi_device *spi, + struct spi_message *m, + gfp_t flags) +{ + int ret; + + if (m->is_optimized) + return -EOPNOTSUPP; + + ret=__spi_validate(spi,m); + if (!ret) { + /* bus-driver specific call goes here */ + m->is_optimized=1; + } + + return ret; +} + +void spi_message_unoptimize(struct spi_device *spi, + struct spi_message *m) +{ + /* bus-driver specific call goes here */ + m->is_optimized=0; +} + static int __spi_async(struct spi_device *spi, struct spi_message *message) { struct spi_master *master = spi->master; @@ -1726,9 +1773,11 @@ int spi_async(struct spi_device *spi, struct spi_message *message) int ret; unsigned long flags; - ret = __spi_validate(spi, message); - if (ret != 0) - return ret; + if (!message->is_optimized) { + ret = __spi_validate(spi, message); + if (ret != 0) + return ret; + } spin_lock_irqsave(&master->bus_lock_spinlock, flags); @@ -1778,9 +1827,11 @@ int spi_async_locked(struct spi_device *spi, struct spi_message *message) int ret; unsigned long flags; - ret = __spi_validate(spi, message); - if (ret != 0) - return ret; + if (!message->is_optimized) { + ret = __spi_validate(spi, message); + if (ret != 0) + return ret; + } spin_lock_irqsave(&master->bus_lock_spinlock, flags); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 8c62ba7..8fa010d 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -506,6 +506,8 @@ extern struct spi_master *spi_busnum_to_master(u16 busnum); * @delay_usecs: microseconds to delay after this transfer before * (optionally) changing the chipselect status, then starting * the next transfer or completing this @spi_message. + * @optimize_hints: bitmask to define which fields are not modified + * after a call to spi_message_optimize * @transfer_list: transfers are sequenced through @spi_message.transfers * * SPI transfers always write the same number of bytes as they read. @@ -585,6 +587,15 @@ struct spi_transfer { u16 delay_usecs; u32 speed_hz; +#define SPI_OPTIMIZE_MAYCHANGE_TX_BUF (1<<0) +#define SPI_OPTIMIZE_MAYCHANGE_RX_BUF (1<<1) + /* these bits apply also to tx_dma/rx_dma if set */ +#define SPI_OPTIMIZE_MAYCHANGE_LEN (1<<2) +#define SPI_OPTIMIZE_MAYCHANGE_DELAY (1<<3) +#define SPI_OPTIMIZE_MAYCHANGE_SPEED (1<<4) +#define SPI_OPTIMIZE_MAYCHANGE_BITS (1<<5) + u32 optimize_hints; + struct list_head transfer_list; }; @@ -594,6 +605,10 @@ struct spi_transfer { * @spi: SPI device to which the transaction is queued * @is_dma_mapped: if true, the caller provided both dma and cpu virtual * addresses for each transfer buffer + * @is_optimized: if true, then the message has been initialized via + * spi_message_optimize - this is (re)set via spi_message_(un)optimize + * @optimize_data: data owned by the bus-driver after spi_message_optimize + * is called * @complete: called to report transaction completions * @context: the argument to complete() when it's called * @actual_length: the total number of bytes that were transferred in all @@ -623,6 +638,9 @@ struct spi_message { unsigned is_dma_mapped:1; + unsigned is_optimized:1; + void *optimize_data; + /* REVISIT: we might want a flag affecting the behavior of the * last transfer ... allowing things like "read 16 bit length L" * immediately followed by "read L bytes". Basically imposing @@ -667,6 +685,13 @@ spi_transfer_del(struct spi_transfer *t) list_del(&t->transfer_list); } +extern int spi_message_optimize(struct spi_device *spi, + struct spi_message *m, + gfp_t flags); + +extern void spi_message_unoptimize(struct spi_device *spi, + struct spi_message *m); + /** * spi_message_init_with_transfers - Initialize spi_message and append transfers * @m: spi_message to be initialized -- To unsubscribe from this list: send the line "unsubscribe linux-spi" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
