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:
Martin Sperl
2015-12-14 15:20:18 +00:00
committed by Mark Brown
parent 92e963f50f
commit d780c3711d
2 changed files with 127 additions and 0 deletions

View File

@@ -1024,6 +1024,8 @@ out:
if (msg->status && master->handle_err)
master->handle_err(master, msg);
spi_res_release(master, msg);
spi_finalize_current_message(master);
return ret;
@@ -2013,6 +2015,95 @@ struct spi_master *spi_busnum_to_master(u16 bus_num)
}
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);
/*-------------------------------------------------------------------------*/