spi: core: added spi_resource management
SPI resource management framework used while processing a spi_message
via the spi-core.
The basic idea is taken from devres, but as the allocation may happen
fairly frequently, some provisioning (in the form of an unused spi_device
pointer argument to spi_res_alloc) has been made so that at a later stage
we may implement reuse objects allocated earlier avoiding the repeated
allocation by keeping a cache of objects that we can reuse.
This framework can get used for:
* rewriting spi_messages
* to fullfill alignment requirements of the spi_master HW
* to fullfill transfer length requirements
(e.g: transfers need to be less than 64k)
* consolidate spi_messages with multiple transfers into a single transfer
when the total transfer length is below a threshold.
* reimplement spi_unmap_buf without explicitly needing to check if it has
been mapped
Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
@@ -1024,6 +1024,8 @@ out:
|
|||||||
if (msg->status && master->handle_err)
|
if (msg->status && master->handle_err)
|
||||||
master->handle_err(master, msg);
|
master->handle_err(master, msg);
|
||||||
|
|
||||||
|
spi_res_release(master, msg);
|
||||||
|
|
||||||
spi_finalize_current_message(master);
|
spi_finalize_current_message(master);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -2013,6 +2015,95 @@ struct spi_master *spi_busnum_to_master(u16 bus_num)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(spi_busnum_to_master);
|
EXPORT_SYMBOL_GPL(spi_busnum_to_master);
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* Core methods for SPI resource management */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spi_res_alloc - allocate a spi resource that is life-cycle managed
|
||||||
|
* during the processing of a spi_message while using
|
||||||
|
* spi_transfer_one
|
||||||
|
* @spi: the spi device for which we allocate memory
|
||||||
|
* @release: the release code to execute for this resource
|
||||||
|
* @size: size to alloc and return
|
||||||
|
* @gfp: GFP allocation flags
|
||||||
|
*
|
||||||
|
* Return: the pointer to the allocated data
|
||||||
|
*
|
||||||
|
* This may get enhanced in the future to allocate from a memory pool
|
||||||
|
* of the @spi_device or @spi_master to avoid repeated allocations.
|
||||||
|
*/
|
||||||
|
void *spi_res_alloc(struct spi_device *spi,
|
||||||
|
spi_res_release_t release,
|
||||||
|
size_t size, gfp_t gfp)
|
||||||
|
{
|
||||||
|
struct spi_res *sres;
|
||||||
|
|
||||||
|
sres = kzalloc(sizeof(*sres) + size, gfp);
|
||||||
|
if (!sres)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&sres->entry);
|
||||||
|
sres->release = release;
|
||||||
|
|
||||||
|
return sres->data;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(spi_res_alloc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spi_res_free - free an spi resource
|
||||||
|
* @res: pointer to the custom data of a resource
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void spi_res_free(void *res)
|
||||||
|
{
|
||||||
|
struct spi_res *sres = container_of(res, struct spi_res, data);
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
return;
|
||||||
|
|
||||||
|
WARN_ON(!list_empty(&sres->entry));
|
||||||
|
kfree(sres);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(spi_res_free);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spi_res_add - add a spi_res to the spi_message
|
||||||
|
* @message: the spi message
|
||||||
|
* @res: the spi_resource
|
||||||
|
*/
|
||||||
|
void spi_res_add(struct spi_message *message, void *res)
|
||||||
|
{
|
||||||
|
struct spi_res *sres = container_of(res, struct spi_res, data);
|
||||||
|
|
||||||
|
WARN_ON(!list_empty(&sres->entry));
|
||||||
|
list_add_tail(&sres->entry, &message->resources);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(spi_res_add);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spi_res_release - release all spi resources for this message
|
||||||
|
* @master: the @spi_master
|
||||||
|
* @message: the @spi_message
|
||||||
|
*/
|
||||||
|
void spi_res_release(struct spi_master *master,
|
||||||
|
struct spi_message *message)
|
||||||
|
{
|
||||||
|
struct spi_res *res;
|
||||||
|
|
||||||
|
while (!list_empty(&message->resources)) {
|
||||||
|
res = list_last_entry(&message->resources,
|
||||||
|
struct spi_res, entry);
|
||||||
|
|
||||||
|
if (res->release)
|
||||||
|
res->release(master, message, res->data);
|
||||||
|
|
||||||
|
list_del(&res->entry);
|
||||||
|
|
||||||
|
kfree(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(spi_res_release);
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|||||||
@@ -582,6 +582,37 @@ extern void spi_unregister_master(struct spi_master *master);
|
|||||||
|
|
||||||
extern struct spi_master *spi_busnum_to_master(u16 busnum);
|
extern struct spi_master *spi_busnum_to_master(u16 busnum);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SPI resource management while processing a SPI message
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct spi_res - spi resource management structure
|
||||||
|
* @entry: list entry
|
||||||
|
* @release: release code called prior to freeing this resource
|
||||||
|
* @data: extra data allocated for the specific use-case
|
||||||
|
*
|
||||||
|
* this is based on ideas from devres, but focused on life-cycle
|
||||||
|
* management during spi_message processing
|
||||||
|
*/
|
||||||
|
typedef void (*spi_res_release_t)(struct spi_master *master,
|
||||||
|
struct spi_message *msg,
|
||||||
|
void *res);
|
||||||
|
struct spi_res {
|
||||||
|
struct list_head entry;
|
||||||
|
spi_res_release_t release;
|
||||||
|
unsigned long long data[]; /* guarantee ull alignment */
|
||||||
|
};
|
||||||
|
|
||||||
|
extern void *spi_res_alloc(struct spi_device *spi,
|
||||||
|
spi_res_release_t release,
|
||||||
|
size_t size, gfp_t gfp);
|
||||||
|
extern void spi_res_add(struct spi_message *message, void *res);
|
||||||
|
extern void spi_res_free(void *res);
|
||||||
|
|
||||||
|
extern void spi_res_release(struct spi_master *master,
|
||||||
|
struct spi_message *message);
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -720,6 +751,7 @@ struct spi_transfer {
|
|||||||
* @status: zero for success, else negative errno
|
* @status: zero for success, else negative errno
|
||||||
* @queue: for use by whichever driver currently owns the message
|
* @queue: for use by whichever driver currently owns the message
|
||||||
* @state: for use by whichever driver currently owns the message
|
* @state: for use by whichever driver currently owns the message
|
||||||
|
* @resources: for resource management when the spi message is processed
|
||||||
*
|
*
|
||||||
* A @spi_message is used to execute an atomic sequence of data transfers,
|
* A @spi_message is used to execute an atomic sequence of data transfers,
|
||||||
* each represented by a struct spi_transfer. The sequence is "atomic"
|
* each represented by a struct spi_transfer. The sequence is "atomic"
|
||||||
@@ -766,11 +798,15 @@ struct spi_message {
|
|||||||
*/
|
*/
|
||||||
struct list_head queue;
|
struct list_head queue;
|
||||||
void *state;
|
void *state;
|
||||||
|
|
||||||
|
/* list of spi_res reources when the spi message is processed */
|
||||||
|
struct list_head resources;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void spi_message_init_no_memset(struct spi_message *m)
|
static inline void spi_message_init_no_memset(struct spi_message *m)
|
||||||
{
|
{
|
||||||
INIT_LIST_HEAD(&m->transfers);
|
INIT_LIST_HEAD(&m->transfers);
|
||||||
|
INIT_LIST_HEAD(&m->resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void spi_message_init(struct spi_message *m)
|
static inline void spi_message_init(struct spi_message *m)
|
||||||
|
|||||||
Reference in New Issue
Block a user