All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/8] spi: spi-message transformation framework
@ 2015-12-14 15:20 kernel-TqfNSX0MhmxHKSADF0wUEw
       [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
  0 siblings, 1 reply; 11+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-12-14 15:20 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Martin Sperl

From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

This patchset implements a spi-message transformation framework in
SPI-core, that allows drivers to transfor individual spi messages
into something that can more easily get handled by the HW.

This would typically be used for HW which has some limitations on
DMA alignment or max transfer sizes.

This patchset implements at this very moment:
* splitting transfers longer than 60k in size into
  multiple transfers.
* splitting misalligned transfers (there are probably multiple
  variations necessary for different HW)
* mergeing multiple small transfers into a single transfer

The patch-set essentially is based arround spi resource
management on a spi_message basis, which was inspired by
devres.

This framework could also get used to handle spi_unmap_buf
calls with the framework.

Right now the drivers themselves have to implement
spi_master.translate_message, but as soon as the "typical"
HW-variations become apparent this may be reduced to assign
one of a set of "standard" translation methods to
spi_master.translate_message instead, but this may require
some additional parametrization of the HW characteristics.

The whole patch set has been tested successfully on a RPI with:
* spi_loopback_test - see below for stats after applying the patch-set
* enc28j60 - note that this driver does do misaligned transfers for
    	     rx and for tx resulting in a need to copy data to align
	     the transfer buffers propperly for DMA.
* fb_st7735r - framebuffer playing BigBuckBunny
* mmc-spi with an out of tree patch to work arround the mmc_spi
  internal dma mapping issues, that inhibits the driver from working
  correctly - this got introduced with commit 0589342c27944e50
   ("of: set dma_mask to point to coherent_dma_mask")

Changelog:
 V1->V2: initial split of different transforms into separate patches
         but only spi_split_transfer_maxsize was implemented
 V2->V3: complete set of transforms implemented in V1 and also merge
         transforms was implemented
         also contains several fixes based on feedback by Andy Shevchenko
	 minimized code required inside spi-bcm2835 removing all of the

spi_loopback_test produces the following statistics:
==> /sys/class/spi_master/spi32766/statistics/bytes
31840476
==> /sys/class/spi_master/spi32766/statistics/bytes_rx
22629003
==> /sys/class/spi_master/spi32766/statistics/bytes_tx
26926636
==> /sys/class/spi_master/spi32766/statistics/errors
0
==> /sys/class/spi_master/spi32766/statistics/messages
1732
==> /sys/class/spi_master/spi32766/statistics/spi_async
0
==> /sys/class/spi_master/spi32766/statistics/spi_sync
1732
==> /sys/class/spi_master/spi32766/statistics/spi_sync_immediate
1732
==> /sys/class/spi_master/spi32766/statistics/timedout
0
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_0-1
610
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_1024-2047
106
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_128-255
170
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_16-31
313
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_16384-32767
0
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_2048-4095
277
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_2-3
286
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_256-511
48
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_32-63
88
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_32768-65535
464
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_4096-8191
287
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_4-7
143
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_512-1023
158
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_64-127
222
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_65536+
0
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_8-15
369
==> /sys/class/spi_master/spi32766/statistics/transfer_bytes_histo_8192-16383
20
==> /sys/class/spi_master/spi32766/statistics/transfers
3561
==> /sys/class/spi_master/spi32766/statistics/transfers_merged
540
==> /sys/class/spi_master/spi32766/statistics/transfers_split_maxsize
320
==> /sys/class/spi_master/spi32766/statistics/transfers_split_unaligned
844
==> /sys/class/spi_master/spi32766/statistics/transfers_split_unaligned_copy
492

Martin Sperl (8):
  spi: core: added spi_resource management
  spi: core: add spi_replace_transfers method
  spi: core: add spi_split_transfers_maxsize
  spi: core: added spi_split_transfers_unaligned
  spi: core: add spi_merge_transfers method
  spi: core: add spi_master.translate_message
  spi: core: add spi_master.min_dma_len and supporting methods
  spi: bcm2835: move to spi-core methods translate_message and can_dma

 drivers/spi/spi-bcm2835.c |   58 +---
 drivers/spi/spi.c         |  848 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h   |  136 +++++++-
 3 files changed, 1002 insertions(+), 40 deletions(-)

--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v3 1/8] spi: core: added spi_resource management
       [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
@ 2015-12-14 15:20   ` kernel-TqfNSX0MhmxHKSADF0wUEw
  2015-12-14 15:20   ` [PATCH v3 2/8] spi: core: add spi_replace_transfers method kernel-TqfNSX0MhmxHKSADF0wUEw
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-12-14 15:20 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Martin Sperl

From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

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-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
 drivers/spi/spi.c       |   91 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h |   36 +++++++++++++++++++
 2 files changed, 127 insertions(+)

Changelog:
  V2 -> V3: contains several fixes based on feedback by Andy Shevchenko

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index d004a3c..8149602 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1025,6 +1025,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;
@@ -2011,6 +2013,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);

 /*-------------------------------------------------------------------------*/

diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 9ea73eb..739a476 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -576,6 +576,37 @@ extern void spi_unregister_master(struct spi_master *master);

 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);
+
 /*---------------------------------------------------------------------------*/

 /*
@@ -714,6 +745,7 @@ struct spi_transfer {
  * @status: zero for success, else negative errno
  * @queue: 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,
  * each represented by a struct spi_transfer.  The sequence is "atomic"
@@ -760,11 +792,15 @@ struct spi_message {
 	 */
 	struct list_head	queue;
 	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)
 {
 	INIT_LIST_HEAD(&m->transfers);
+	INIT_LIST_HEAD(&m->resources);
 }

 static inline void spi_message_init(struct spi_message *m)
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 2/8] spi: core: add spi_replace_transfers method
       [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
  2015-12-14 15:20   ` [PATCH v3 1/8] spi: core: added spi_resource management kernel-TqfNSX0MhmxHKSADF0wUEw
@ 2015-12-14 15:20   ` kernel-TqfNSX0MhmxHKSADF0wUEw
  2015-12-14 15:20   ` [PATCH v3 3/8] spi: core: add spi_split_transfers_maxsize kernel-TqfNSX0MhmxHKSADF0wUEw
                     ` (5 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-12-14 15:20 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Martin Sperl

From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

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-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
 drivers/spi/spi.c       |  132 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h |   45 ++++++++++++++++
 2 files changed, 177 insertions(+)

Changelog:
  V2 -> V3: new implementation of spi_replace_transfers

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 8149602..ede3e57 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2105,6 +2105,138 @@ EXPORT_SYMBOL_GPL(spi_res_release);

 /*-------------------------------------------------------------------------*/

+/* Core methods for spi_message alterations */
+
+static void __spi_replace_transfers_release(struct spi_master *master,
+					    struct spi_message *msg,
+					    void *res)
+{
+	struct spi_replaced_transfers *rxfer = res;
+	size_t i;
+
+	/* call extra callback if requested */
+	if (rxfer->release)
+		rxfer->release(master, msg, res);
+
+	/* insert replaced transfers back into the message */
+	list_splice(&rxfer->replaced_transfers, rxfer->replaced_after);
+
+	/* remove the formerly inserted entries */
+	for (i = 0; i < rxfer->inserted; i++)
+		list_del(&rxfer->inserted_transfers[i].transfer_list);
+}
+
+/**
+ * spi_replace_transfers - replace transfers with several transfers
+ *                         and register change with spi_message.resources
+ * @msg:           the spi_message we work upon
+ * @xfer_first:    the first spi_transfer we want to replace
+ * @remove:        number of transfers to remove
+ * @insert:        the number of transfers we want to insert instead
+ * @release:       extra release code necessary in some circumstances
+ * @extradatasize: extra data to allocate (with alignment guarantees
+ *                 of struct @spi_transfer)
+ *
+ * Returns: pointer to @spi_replaced_transfers,
+ *          PTR_ERR(...) in case of errors.
+ */
+struct spi_replaced_transfers *spi_replace_transfers(
+	struct spi_message *msg,
+	struct spi_transfer *xfer_first,
+	size_t remove,
+	size_t insert,
+	spi_replaced_release_t release,
+	size_t extradatasize,
+	gfp_t gfp)
+{
+	struct spi_replaced_transfers *rxfer;
+	struct spi_transfer *xfer;
+	size_t i;
+
+	/* allocate the structure using spi_res */
+	rxfer = spi_res_alloc(msg->spi, __spi_replace_transfers_release,
+			      insert * sizeof(struct spi_transfer)
+			      + sizeof(struct spi_replaced_transfers)
+			      + extradatasize,
+			      gfp);
+	if (!rxfer)
+		return ERR_PTR(-ENOMEM);
+
+	/* the release code to invoke before running the generic release */
+	rxfer->release = release;
+
+	/* assign extradata */
+	if (extradatasize)
+		rxfer->extradata =
+			&rxfer->inserted_transfers[insert];
+
+	/* init the replaced_transfers list */
+	INIT_LIST_HEAD(&rxfer->replaced_transfers);
+
+	/* assign the list_entry after which we should reinsert
+	 * the @replaced_transfers - it may be spi_message.messages!
+	 */
+	rxfer->replaced_after = xfer_first->transfer_list.prev;
+
+	/* remove the requested number of transfers */
+	for (i = 0; i < remove; i++) {
+		/* if the entry after replaced_after it is msg->transfers
+		 * then we have been requested to remove more transfers
+		 * than are in the list
+		 */
+		if (rxfer->replaced_after->next == &msg->transfers) {
+			dev_err(&msg->spi->dev,
+				"requested to remove more spi_transfers than are available\n");
+			/* insert replaced transfers back into the message */
+			list_splice(&rxfer->replaced_transfers,
+				    rxfer->replaced_after);
+
+			/* free the spi_replace_transfer structure */
+			spi_res_free(rxfer);
+
+			/* and return with an error */
+			return ERR_PTR(-EINVAL);
+		}
+
+		/* remove the entry after replaced_after from list of
+		 * transfers and add it to list of replaced_transfers
+		 */
+		list_move_tail(rxfer->replaced_after->next,
+			       &rxfer->replaced_transfers);
+	}
+
+	/* create copy of the given xfer with identical settings
+	 * based on the first transfer to get removed
+	 */
+	for (i = 0; i < insert; i++) {
+		/* we need to run in reverse order */
+		xfer = &rxfer->inserted_transfers[insert - 1 - i];
+
+		/* copy all spi_transfer data */
+		memcpy(xfer, xfer_first, sizeof(*xfer));
+
+		/* add to list */
+		list_add(&xfer->transfer_list, rxfer->replaced_after);
+
+		/* clear cs_change and delay_usecs for all but the last */
+		if (i) {
+			xfer->cs_change = false;
+			xfer->delay_usecs = 0;
+		}
+	}
+
+	/* set up inserted */
+	rxfer->inserted = insert;
+
+	/* and register it with spi_res/spi_message */
+	spi_res_add(msg, rxfer);
+
+	return rxfer;
+}
+EXPORT_SYMBOL_GPL(spi_replace_transfers);
+
+/*-------------------------------------------------------------------------*/
+
 /* Core methods for SPI master protocol drivers.  Some of the
  * other core methods are currently defined as inline functions.
  */
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 739a476..13831ed 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -875,6 +875,51 @@ extern int spi_async_locked(struct spi_device *spi,

 /*---------------------------------------------------------------------------*/

+/* SPI transfer replacement methods which make use of spi_res */
+
+/**
+ * struct spi_replaced_transfers - structure describing the spi_transfer
+ *                                 replacements that have occurred
+ *                                 so that they can get reverted
+ * @release:            some extra release code to get executed prior to
+ *                      relasing this structure
+ * @extradata:          pointer to some extra data if requested or NULL
+ * @replaced_transfers: transfers that have been replaced and which need
+ *                      to get restored
+ * @replaced_after:     the transfer after which the @replaced_transfers
+ *                      are to get re-inserted
+ * @inserted:           number of transfers inserted
+ * @inserted_transfers: array of spi_transfers of array-size @inserted,
+ *                      that have been replacing replaced_transfers
+ *
+ * note: that @extradata will point to @inserted_transfers[@inserted]
+ * if some extra allocation is requested, so alignment will be the same
+ * as for spi_transfers
+ */
+struct spi_replaced_transfers;
+typedef void (*spi_replaced_release_t)(struct spi_master *master,
+				       struct spi_message *msg,
+				       struct spi_replaced_transfers *res);
+struct spi_replaced_transfers {
+	spi_replaced_release_t release;
+	void *extradata;
+	struct list_head replaced_transfers;
+	struct list_head *replaced_after;
+	size_t inserted;
+	struct spi_transfer inserted_transfers[];
+};
+
+extern struct spi_replaced_transfers *spi_replace_transfers(
+	struct spi_message *msg,
+	struct spi_transfer *xfer_first,
+	size_t remove,
+	size_t insert,
+	spi_replaced_release_t release,
+	size_t extradatasize,
+	gfp_t gfp);
+
+/*---------------------------------------------------------------------------*/
+
 /* All these synchronous SPI transfer routines are utilities layered
  * over the core async transfer primitive.  Here, "synchronous" means
  * they will sleep uninterruptibly until the async transfer completes.
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 3/8] spi: core: add spi_split_transfers_maxsize
       [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
  2015-12-14 15:20   ` [PATCH v3 1/8] spi: core: added spi_resource management kernel-TqfNSX0MhmxHKSADF0wUEw
  2015-12-14 15:20   ` [PATCH v3 2/8] spi: core: add spi_replace_transfers method kernel-TqfNSX0MhmxHKSADF0wUEw
@ 2015-12-14 15:20   ` kernel-TqfNSX0MhmxHKSADF0wUEw
       [not found]     ` <1450106426-2277-4-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
  2015-12-14 15:20   ` [PATCH v3 4/8] spi: core: added spi_split_transfers_unaligned kernel-TqfNSX0MhmxHKSADF0wUEw
                     ` (4 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-12-14 15:20 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Martin Sperl

From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

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-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
 drivers/spi/spi.c       |  111 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h |   15 +++++++
 2 files changed, 126 insertions(+)

Changelog:
  V2 -> V3: split into distinct patches and rewrite

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ede3e57..37e6507 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -145,6 +145,8 @@ SPI_STATISTICS_TRANSFER_BYTES_HISTO(14, "16384-32767");
 SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535");
 SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+");

+SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu");
+
 static struct attribute *spi_dev_attrs[] = {
 	&dev_attr_modalias.attr,
 	NULL,
@@ -182,6 +184,7 @@ static struct attribute *spi_device_statistics_attrs[] = {
 	&dev_attr_spi_device_transfer_bytes_histo14.attr,
 	&dev_attr_spi_device_transfer_bytes_histo15.attr,
 	&dev_attr_spi_device_transfer_bytes_histo16.attr,
+	&dev_attr_spi_device_transfers_split_maxsize.attr,
 	NULL,
 };

@@ -224,6 +227,7 @@ static struct attribute *spi_master_statistics_attrs[] = {
 	&dev_attr_spi_master_transfer_bytes_histo14.attr,
 	&dev_attr_spi_master_transfer_bytes_histo15.attr,
 	&dev_attr_spi_master_transfer_bytes_histo16.attr,
+	&dev_attr_spi_master_transfers_split_maxsize.attr,
 	NULL,
 };

@@ -2235,6 +2239,113 @@ struct spi_replaced_transfers *spi_replace_transfers(
 }
 EXPORT_SYMBOL_GPL(spi_replace_transfers);

+int __spi_split_transfer_maxsize(struct spi_master *master,
+				 struct spi_message *msg,
+				 struct spi_transfer **xferp,
+				 size_t maxsize,
+				 gfp_t gfp)
+{
+	struct spi_transfer *xfer = *xferp, *xfers;
+	struct spi_replaced_transfers *srt;
+	size_t offset;
+	size_t count, i;
+
+	/* warn once about this fact that we are splitting a transfer */
+	dev_warn_once(&msg->spi->dev,
+		      "spi_transfer of length %i exceed max length of %i - needed to split transfers\n",
+		      xfer->len, maxsize);
+
+	/* calculate how many we have to replace */
+	count = DIV_ROUND_UP(xfer->len, maxsize);
+
+	/* create replacement */
+	srt = spi_replace_transfers(msg, xfer, 1, count, NULL, 0, gfp);
+	if (!srt)
+		return -ENOMEM;
+	xfers = srt->inserted_transfers;
+
+	/* now handle each of those newly inserted spi_transfers
+	 * note that the replacements spi_transfers all are preset
+	 * to the same values as *xferp, so tx_buf, rx_buf and len
+	 * are all identical (as well as most others)
+	 * so we just have to fix up len and the pointers.
+	 *
+	 * this also includes support for the depreciated
+	 * spi_message.is_dma_mapped interface
+	 */
+
+	/* the first transfer just needs the length modified, so we
+	 * run it outside the loop
+	 */
+	xfers[0].len = min(maxsize, xfer[0].len);
+
+	/* all the others need rx_buf/tx_buf also set */
+	for (i = 1, offset = maxsize; i < count; offset += maxsize, i++) {
+		/* update rx_buf, tx_buf and dma */
+		if (xfers[i].rx_buf)
+			xfers[i].rx_buf += offset;
+		if (xfers[i].rx_dma)
+			xfers[i].rx_dma += offset;
+		if (xfers[i].tx_buf)
+			xfers[i].tx_buf += offset;
+		if (xfers[i].tx_dma)
+			xfers[i].tx_dma += offset;
+
+		/* update length */
+		xfers[i].len = min(maxsize, xfers[i].len - offset);
+	}
+
+	/* we set up xferp to the last entry we have inserted,
+	 * so that we skip those already split transfers
+	 */
+	*xferp = &xfers[count - 1];
+
+	/* increment statistics counters */
+	SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
+				       transfers_split_maxsize);
+	SPI_STATISTICS_INCREMENT_FIELD(&msg->spi->statistics,
+				       transfers_split_maxsize);
+
+	return 0;
+}
+
+/**
+ * spi_split_tranfers_maxsize - split spi transfers into multiple transfers
+ *                              when an individual transfer exceeds a
+ *                              certain size
+ * @master:    the @spi_master for this transfer
+ * @message:   the @spi_message to transform
+ * @max_size:  the maximum when to apply this
+ *
+ * Return: status of transformation
+ */
+int spi_split_transfers_maxsize(struct spi_master *master,
+				struct spi_message *msg,
+				size_t maxsize,
+				gfp_t gfp)
+{
+	struct spi_transfer *xfer;
+	int ret;
+
+	/* iterate over the transfer_list,
+	 * but note that xfer is advanced to the last transfer inserted
+	 * to avoid checking sizes again unnecessarily (also xfer does
+	 * potentiall belong to a different list by the time the
+	 * replacement has happened
+	 */
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+		if (xfer->len > maxsize) {
+			ret = __spi_split_transfer_maxsize(
+				master, msg, &xfer, maxsize, gfp);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_split_transfers_maxsize);
+
 /*-------------------------------------------------------------------------*/

 /* 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 13831ed..8ced84a 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -53,6 +53,10 @@ extern struct bus_type spi_bus_type;
  *
  * @transfer_bytes_histo:
  *                 transfer bytes histogramm
+ *
+ * @transfers_split_maxsize:
+ *                 number of transfers that have been split because of
+ *                 maxsize limit
  */
 struct spi_statistics {
 	spinlock_t		lock; /* lock for the whole structure */
@@ -72,6 +76,8 @@ struct spi_statistics {

 #define SPI_STATISTICS_HISTO_SIZE 17
 	unsigned long transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE];
+
+	unsigned long transfers_split_maxsize;
 };

 void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
@@ -920,6 +926,15 @@ extern struct spi_replaced_transfers *spi_replace_transfers(

 /*---------------------------------------------------------------------------*/

+/* SPI transfer transformation methods */
+
+extern int spi_split_transfers_maxsize(struct spi_master *master,
+				       struct spi_message *msg,
+				       size_t maxsize,
+				       gfp_t gfp);
+
+/*---------------------------------------------------------------------------*/
+
 /* All these synchronous SPI transfer routines are utilities layered
  * over the core async transfer primitive.  Here, "synchronous" means
  * they will sleep uninterruptibly until the async transfer completes.
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 4/8] spi: core: added spi_split_transfers_unaligned
       [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
                     ` (2 preceding siblings ...)
  2015-12-14 15:20   ` [PATCH v3 3/8] spi: core: add spi_split_transfers_maxsize kernel-TqfNSX0MhmxHKSADF0wUEw
@ 2015-12-14 15:20   ` kernel-TqfNSX0MhmxHKSADF0wUEw
       [not found]     ` <1450106426-2277-5-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
  2015-12-14 15:20   ` [PATCH v3 5/8] spi: core: add spi_merge_transfers method kernel-TqfNSX0MhmxHKSADF0wUEw
                     ` (3 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-12-14 15:20 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Martin Sperl

From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

Added code that splits a single spi_transfer into 2 transfers
in case either transfer-buffer is not aligned.

In the case of rx and tx_buf misaligned to different offsets,
the tx_buf is also copied to an aligned address so that the
whole transfer is aligned on both buffers.

We chose tx_buf to get copied because this may allow us to
use CPU resources (or different cores) in parallel to an
already running transfer in the future and avoids a
syncronous copy after the transfer is finished.

Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
 drivers/spi/spi.c       |  206 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h |   13 +++
 2 files changed, 219 insertions(+)

Changelog:
  V1 -> V3: split into distinct patches and rewrite to allow for copy

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 37e6507..f276c99 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -146,6 +146,8 @@ SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535");
 SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+");

 SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu");
+SPI_STATISTICS_SHOW(transfers_split_unaligned, "%lu");
+SPI_STATISTICS_SHOW(transfers_split_unaligned_copy, "%lu");

 static struct attribute *spi_dev_attrs[] = {
 	&dev_attr_modalias.attr,
@@ -185,6 +187,8 @@ static struct attribute *spi_device_statistics_attrs[] = {
 	&dev_attr_spi_device_transfer_bytes_histo15.attr,
 	&dev_attr_spi_device_transfer_bytes_histo16.attr,
 	&dev_attr_spi_device_transfers_split_maxsize.attr,
+	&dev_attr_spi_device_transfers_split_unaligned.attr,
+	&dev_attr_spi_device_transfers_split_unaligned_copy.attr,
 	NULL,
 };

@@ -228,6 +232,8 @@ static struct attribute *spi_master_statistics_attrs[] = {
 	&dev_attr_spi_master_transfer_bytes_histo15.attr,
 	&dev_attr_spi_master_transfer_bytes_histo16.attr,
 	&dev_attr_spi_master_transfers_split_maxsize.attr,
+	&dev_attr_spi_master_transfers_split_unaligned.attr,
+	&dev_attr_spi_master_transfers_split_unaligned_copy.attr,
 	NULL,
 };

@@ -2346,6 +2352,206 @@ int spi_split_transfers_maxsize(struct spi_master *master,
 }
 EXPORT_SYMBOL_GPL(spi_split_transfers_maxsize);

+static int __spi_split_transfer_unaligned_do(
+	struct spi_master *master,
+	struct spi_message *msg,
+	struct spi_transfer **xferp,
+	size_t alignment,
+	size_t alignment_to_correct,
+	bool copy_tx,
+	gfp_t gfp)
+{
+	struct spi_transfer *xfers;
+	struct spi_replaced_transfers *srt;
+	size_t alignment_shift;
+	size_t extra = 0;
+	void *data;
+
+	/* warn once about this fact that we are splitting */
+	dev_warn_once(&msg->spi->dev,
+		      "spi_transfer with misaligned buffers - had to split transfers\n");
+
+	/* the number of bytes by which we need to shift to align */
+	alignment_shift = BIT(alignment) - alignment_to_correct;
+
+	/* calculate extra buffer we need to allocate to align tx by copy */
+	if (copy_tx)
+		extra = (*xferp)->len - alignment_shift + BIT(alignment);
+
+	/* replace the transfer */
+	srt = spi_replace_transfers(msg, *xferp, 1, 2, NULL, extra, gfp);
+	if (!srt)
+		return -ENOMEM;
+	xfers = srt->inserted_transfers;
+
+	/* for the first transfer we only set the length */
+	xfers[0].len = alignment_shift;
+
+	/* for the second transfer we need to shift the pointers as well
+	 * as modify length (substracting the len we transfer in the first)
+	 */
+	xfers[1].len -= alignment_shift;
+	if (xfers[1].tx_buf)
+		xfers[1].tx_buf += alignment_shift;
+	if (xfers[1].rx_buf)
+		xfers[1].rx_buf += alignment_shift;
+	if (xfers[1].tx_dma)
+		xfers[1].tx_dma += alignment_shift;
+	if (xfers[1].rx_dma)
+		xfers[1].rx_dma += alignment_shift;
+
+	/* handle copy_tx  */
+	if (copy_tx) {
+		data = PTR_ALIGN(srt->extradata, alignment);
+
+		/* copy data from old to new */
+		memcpy(data, xfers[1].tx_buf, xfers[1].len);
+
+		/* set up the pointer to the new buffer */
+		xfers[1].tx_buf = data;
+
+		/* force the tx_dma/rx_dma buffers to be unset */
+		xfers[1].tx_dma = 0;
+		xfers[1].rx_dma = 0;
+
+		/* warn once about this fact that we are also copying */
+		dev_warn_once(&msg->spi->dev, "spi_transfer rx/tx buffers are misaligned to different offsets - need to copy tx_buf to fix it\n");
+		/* increment statistics counters */
+		SPI_STATISTICS_INCREMENT_FIELD(
+			&master->statistics,
+			transfers_split_unaligned_copy);
+		SPI_STATISTICS_INCREMENT_FIELD(
+			&msg->spi->statistics,
+			transfers_split_unaligned_copy);
+	}
+
+	/* finally we can set up xferp as xfers[1] */
+	*xferp = &xfers[1];
+
+	/* increment statistics counters */
+	SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
+				       transfers_split_unaligned);
+	SPI_STATISTICS_INCREMENT_FIELD(&msg->spi->statistics,
+				       transfers_split_unaligned);
+
+	return 0;
+}
+
+static int __spi_split_transfer_unaligned_check(
+	struct spi_master *master,
+	struct spi_message *msg,
+	struct spi_transfer **xferp,
+	size_t alignment,
+	gfp_t gfp)
+{
+	struct spi_transfer *xfer = *xferp;
+	size_t alignment_mask;
+
+	const char *tx_start, *rx_start; /* the rx/tx_buf address */
+	const char *tx_end, *rx_end; /* the last byte of the transfer */
+	size_t tx_start_page, rx_start_page; /* the "page address" for start */
+	size_t tx_end_page, rx_end_page; /* the "page address" for end */
+	size_t tx_start_align, rx_start_align; /* alignment of buf address */
+
+	/* calculate the values */
+	alignment_mask = (1 << alignment) - 1;
+
+	tx_start = xfer->tx_buf;
+	tx_start_align = ((size_t)tx_start & alignment_mask);
+
+	rx_start = xfer->rx_buf;
+	rx_start_align = ((size_t)rx_start & alignment_mask);
+
+	/* if the start alignment is 0 for both rx and tx */
+	if ((!rx_start_align) && (!tx_start_align))
+		return 0;
+
+	/* the end pointer */
+	tx_end = tx_start ? &tx_start[xfer->len - 1] : NULL;
+	rx_end = rx_start ? &tx_start[xfer->len - 1] : NULL;
+
+	/* and the page addresses - mostly needed to see if the transfer
+	 * spills over a page boundary
+	 */
+	tx_start_page = (size_t)tx_start & (PAGE_SIZE - 1);
+	tx_end_page = (size_t)tx_end & (PAGE_SIZE - 1);
+	rx_start_page = (size_t)rx_start & (PAGE_SIZE - 1);
+	rx_end_page = (size_t)rx_end & (PAGE_SIZE - 1);
+
+	/* if we are on the same end-page, then there is nothing to do */
+	if ((tx_start_page == tx_end_page) &&
+	    (rx_start_page == rx_end_page))
+		return 0;
+
+	/* if tx_align (not 0 means also not null) and rx_start is null */
+	if (tx_start_align && (!rx_start))
+		return __spi_split_transfer_unaligned_do(
+			master, msg, xferp, alignment, tx_start_align,
+			false, gfp);
+
+	/* if rx_align (not 0 means also not null) and tx_start is null */
+	if (rx_start_align && (!tx_start))
+		return __spi_split_transfer_unaligned_do(
+			master, msg, xferp, alignment, rx_start_align,
+			false, gfp);
+
+	/* if the alignment for both is identical */
+	if (rx_start_align == tx_start_align)
+		return __spi_split_transfer_unaligned_do(
+			master, msg, xferp, alignment, rx_start_align,
+			false, gfp);
+
+	/* for the final case with tx and rx of different alignment
+	 * we just align rx and copy tx to an alligned transfer
+	 */
+	return __spi_split_transfer_unaligned_do(
+		master, msg, xferp, alignment, rx_start_align, true, gfp);
+}
+
+/**
+ * spi_split_tranfers_unaligned - split spi transfers into multiple transfers
+ *                                when rx_buf or tx_buf are unaligned
+ *                                and the transfer size is above minimum
+ * @master:    the @spi_master for this transfer
+ * @message:   the @spi_message to transform
+ * @min_size:  the minimum size when to apply this
+ * @alignment: the alignment  that we require
+ * @gfp:       gfp flags
+ *
+ * Return: status of transformation
+ *
+ * note that the "first" transfer is always unaligned,
+ * but its length is always < (1 << alignment) - this assumes that
+ * the first transfer gets done in PIO mode
+ */
+int spi_split_transfers_unaligned(struct spi_master *master,
+				  struct spi_message *message,
+				  size_t min_size,
+				  size_t alignment,
+				  gfp_t gfp)
+{
+	struct spi_transfer *xfer;
+	int ret;
+
+	/* iterate over the transfer_list,
+	 * but note that xfer is advanced to the last transfer inserted
+	 * to avoid checking sizes again unnecessarily (also xfer does
+	 * potentiall belong to a different list by the time the
+	 * replacement has happened
+	 */
+	list_for_each_entry(xfer, &message->transfers, transfer_list) {
+		if (xfer->len >= min_size) {
+			ret = __spi_split_transfer_unaligned_check(
+				master, message, &xfer, alignment, gfp);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_split_transfers_unaligned);
+
 /*-------------------------------------------------------------------------*/

 /* 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 8ced84a..deb94a3 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -57,6 +57,12 @@ extern struct bus_type spi_bus_type;
  * @transfers_split_maxsize:
  *                 number of transfers that have been split because of
  *                 maxsize limit
+ * @transfers_split_unaligned:
+ *                 number of transfers that have been split because of
+ *                 alignment issues
+ * @transfers_split_unaligned_copy:
+ *                 number of transfers that have been aligned by copying
+ *                 at least one buffer (typically tx)
  */
 struct spi_statistics {
 	spinlock_t		lock; /* lock for the whole structure */
@@ -78,6 +84,8 @@ struct spi_statistics {
 	unsigned long transfer_bytes_histo[SPI_STATISTICS_HISTO_SIZE];

 	unsigned long transfers_split_maxsize;
+	unsigned long transfers_split_unaligned;
+	unsigned long transfers_split_unaligned_copy;
 };

 void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
@@ -932,6 +940,11 @@ extern int spi_split_transfers_maxsize(struct spi_master *master,
 				       struct spi_message *msg,
 				       size_t maxsize,
 				       gfp_t gfp);
+extern int spi_split_transfers_unaligned(struct spi_master *master,
+					 struct spi_message *msg,
+					 size_t min_size,
+					 size_t alignment,
+					 gfp_t gfp);

 /*---------------------------------------------------------------------------*/

--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 5/8] spi: core: add spi_merge_transfers method
       [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
                     ` (3 preceding siblings ...)
  2015-12-14 15:20   ` [PATCH v3 4/8] spi: core: added spi_split_transfers_unaligned kernel-TqfNSX0MhmxHKSADF0wUEw
@ 2015-12-14 15:20   ` kernel-TqfNSX0MhmxHKSADF0wUEw
  2015-12-14 15:20   ` [PATCH v3 6/8] spi: core: add spi_master.translate_message kernel-TqfNSX0MhmxHKSADF0wUEw
                     ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-12-14 15:20 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Martin Sperl

From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

Added method spi_merge_transfers which consolidates multiple
spi_transfers into a single spi_transfer making use of
spi_res for management of resources/reverting changes to the
spi_message structure.

Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
 drivers/spi/spi.c       |  227 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h |    9 ++
 2 files changed, 236 insertions(+)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index f276c99..fe3b18e 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -148,6 +148,7 @@ SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+");
 SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu");
 SPI_STATISTICS_SHOW(transfers_split_unaligned, "%lu");
 SPI_STATISTICS_SHOW(transfers_split_unaligned_copy, "%lu");
+SPI_STATISTICS_SHOW(transfers_merged, "%lu");
 
 static struct attribute *spi_dev_attrs[] = {
 	&dev_attr_modalias.attr,
@@ -189,6 +190,7 @@ static struct attribute *spi_device_statistics_attrs[] = {
 	&dev_attr_spi_device_transfers_split_maxsize.attr,
 	&dev_attr_spi_device_transfers_split_unaligned.attr,
 	&dev_attr_spi_device_transfers_split_unaligned_copy.attr,
+	&dev_attr_spi_device_transfers_merged.attr,
 	NULL,
 };
 
@@ -234,6 +236,7 @@ static struct attribute *spi_master_statistics_attrs[] = {
 	&dev_attr_spi_master_transfers_split_maxsize.attr,
 	&dev_attr_spi_master_transfers_split_unaligned.attr,
 	&dev_attr_spi_master_transfers_split_unaligned_copy.attr,
+	&dev_attr_spi_master_transfers_merged.attr,
 	NULL,
 };
 
@@ -2552,6 +2555,230 @@ int spi_split_transfers_unaligned(struct spi_master *master,
 }
 EXPORT_SYMBOL_GPL(spi_split_transfers_unaligned);
 
+static void __spi_merge_transfers_release(struct spi_master *master,
+					  struct spi_message *msg,
+					  struct spi_replaced_transfers *srt)
+{
+	u8 *ptr = srt->inserted_transfers[0].rx_buf;
+	struct spi_transfer *xfer;
+
+	/* copy the data to the final locations */
+	list_for_each_entry(xfer, &srt->replaced_transfers, transfer_list) {
+		/* fill data only if rx_buf is set */
+		if (xfer->rx_buf)
+			memcpy(xfer->rx_buf, ptr, xfer->len);
+		/* update the pointer for next loop */
+		ptr += xfer->len;
+	}
+}
+
+static int __spi_merge_transfers_do(struct spi_master *master,
+				    struct spi_message *msg,
+				    struct spi_transfer **xfer_start,
+				    struct spi_transfer *xfer_end,
+				    int count,
+				    size_t size,
+				    gfp_t gfp)
+{
+	size_t align = master->dma_alignment ? : sizeof(int);
+	size_t align_overhead = BIT(align) - 1;
+	size_t size_to_alloc = size + align_overhead;
+	struct spi_transfer *xfer_new, *xfer;
+	struct spi_replaced_transfers *srt;
+	u8 *data_tx;
+	int i;
+
+	/* for anything but 3-wire mode we need rx/tx_buf to be set */
+	if (!(msg->spi->mode & SPI_3WIRE))
+		size_to_alloc *= 2;
+
+	/* replace count transfers starting with xfer_start
+	 * and replace them with a single transfer (which is returned)
+	 * the extra data requeste starts at:
+	 *   returned pointer + sizeof(struct_transfer)
+	 */
+	srt = spi_replace_transfers(msg, *xfer_start, count, 1,
+				    __spi_merge_transfers_release,
+				    size_to_alloc, gfp);
+	if (!srt)
+		return -ENOMEM;
+
+	/* the inserted transfer */
+	xfer_new = srt->inserted_transfers;
+
+	/* pointer to data_tx data allocated - aligned */
+	data_tx = PTR_ALIGN(srt->extradata, align);
+
+	/* fill the transfer with the settings from the last replaced */
+	xfer_new->cs_change = xfer_end->cs_change;
+	xfer_new->delay_usecs = xfer_end->delay_usecs;
+
+	/* set the size of the transfer */
+	xfer_new->len = size;
+
+	/* now fill in the corresponding aligned pointers */
+	if (msg->spi->mode & SPI_3WIRE) {
+		/* if the first transfer has tx_buf set,
+		 * then we are transmitting
+		 */
+		if ((*xfer_start)->tx_buf) {
+			xfer_new->tx_buf = data_tx;
+			xfer_new->rx_buf = NULL;
+		} else {
+			xfer_new->tx_buf = NULL;
+			xfer_new->rx_buf = data_tx;
+		}
+	} else {
+		xfer_new->tx_buf = data_tx;
+		xfer_new->rx_buf = PTR_ALIGN(data_tx + size, align);
+	}
+
+	/* copy the data we need for tx (if it is set) */
+	if (xfer_new->tx_buf) {
+		for (xfer = *xfer_start, i = 0;
+		    i < count;
+		    i++, xfer = list_next_entry(xfer, transfer_list)) {
+			/* fill data only if tx_buf is set */
+			if (xfer->tx_buf)
+				memcpy(data_tx, xfer->tx_buf, xfer->len);
+			/* update pointer to after memcpy for next loop */
+			data_tx += xfer->len;
+		}
+	}
+
+	/* update the transfer to the one we have just put into the list */
+	*xfer_start = xfer_new;
+
+	/* increment statistics counters */
+	SPI_STATISTICS_ADD_TO_FIELD(&master->statistics,
+				    transfers_merged, count);
+	SPI_STATISTICS_ADD_TO_FIELD(&msg->spi->statistics,
+				    transfers_merged, count);
+
+	return 0;
+}
+
+static int __spi_merge_transfers(struct spi_master *master,
+				 struct spi_message *msg,
+				 struct spi_transfer **xfer_start,
+				 size_t min_size,
+				 size_t max_size,
+				 gfp_t gfp)
+{
+	struct spi_transfer *xfer, *xfer_end;
+	int count;
+	size_t size;
+
+	/* loop transfers until we reach
+	 * * the end of the list
+	 * * a change in some essential parameters in spi_transfer
+	 *   compared to the first transfer we check
+	 *   (speed, bits, direction in 3 wire mode)
+	 * * settings that immediately indicate we need to stop testing
+	 *   the next transfer (cs_change, delay_usecs)
+	 */
+	for (count = 0, size = 0, xfer = *xfer_start, xfer_end = xfer;
+	     !list_is_last(&xfer->transfer_list, &msg->transfers);
+	     xfer = list_next_entry(xfer, transfer_list)) {
+		/* now check on total size */
+		if (size + xfer->len > max_size)
+			break;
+		/* these checks are only necessary on subsequent transfers */
+		if (count) {
+			/* check if we differ from the first transfer */
+			if (xfer->speed_hz != (*xfer_start)->speed_hz)
+				break;
+			if (xfer->tx_nbits != (*xfer_start)->tx_nbits)
+				break;
+			if (xfer->rx_nbits != (*xfer_start)->rx_nbits)
+				break;
+			if (xfer->bits_per_word !=
+			    (*xfer_start)->bits_per_word)
+				break;
+
+			/* 3-wire we need to handle in a special way */
+			if (msg->spi->mode & SPI_3WIRE) {
+				/* did we switch directions in 3 wire mode ? */
+				if (xfer->tx_buf && (*xfer_start)->rx_buf)
+					break;
+				if (xfer->rx_buf && (*xfer_start)->tx_buf)
+					break;
+			}
+		}
+		/* otherwise update counters for the last few tests,
+		 * that only depend on settings of the current transfer
+		 */
+		count++;
+		size += xfer->len;
+		xfer_end = xfer;
+
+		/* check for conditions that would trigger a merge
+		 * based only on the current transfer
+		 * so we need count and size updated already...
+		 */
+		if (xfer->cs_change)
+			break;
+		if (xfer->delay_usecs)
+			break;
+	}
+
+	/* merge only when we have at least 2 transfers to handle */
+	if (count < 2)
+		return 0;
+
+	/* and also only if we really have reached our min_size */
+	if (size < min_size)
+		return 0;
+
+	/* do the transformation for real now */
+	return __spi_merge_transfers_do(master, msg,
+					xfer_start, xfer_end,
+					count, size, gfp);
+}
+
+/**
+ * spi_merge_tranfers - merges multiple spi_transfers into a single one
+ *                      copying the corresponding data
+ * @master:    the @spi_master for this transfer
+ * @message:   the @spi_message to transform
+ * @max_size:  the minimum total length when to apply this transform
+ * @max_size:  the maximum total length when to apply this transform
+ * @gfp:       gfp flags
+ *
+ * Return: status of transformation
+ */
+int spi_merge_transfers(struct spi_master *master,
+			struct spi_message *msg,
+			size_t min_size,
+			size_t max_size,
+			gfp_t gfp)
+{
+	struct spi_transfer *xfer;
+	int ret;
+
+	/* nothing to merge if the list is empty */
+	if (list_is_singular(&msg->transfers))
+		return 0;
+
+	/* if the total transfer size is too small, then skip */
+	if (msg->frame_length < min_size)
+		return 0;
+
+	/* iterate over all transfers and modify xfer if we have
+	 * replaced some transfers
+	 */
+	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
+		/* test if it is a merge candidate */
+		ret = __spi_merge_transfers(master, msg, &xfer,
+					    min_size, max_size, gfp);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_merge_transfers);
+
 /*-------------------------------------------------------------------------*/
 
 /* 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 deb94a3..7172e73 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -63,6 +63,9 @@ extern struct bus_type spi_bus_type;
  * @transfers_split_unaligned_copy:
  *                 number of transfers that have been aligned by copying
  *                 at least one buffer (typically tx)
+ * @transfers_merged:
+ *                 number of transfers that have been merged to allow
+ *                 for better efficiency using dma
  */
 struct spi_statistics {
 	spinlock_t		lock; /* lock for the whole structure */
@@ -86,6 +89,7 @@ struct spi_statistics {
 	unsigned long transfers_split_maxsize;
 	unsigned long transfers_split_unaligned;
 	unsigned long transfers_split_unaligned_copy;
+	unsigned long transfers_merged;
 };
 
 void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
@@ -945,6 +949,11 @@ extern int spi_split_transfers_unaligned(struct spi_master *master,
 					 size_t min_size,
 					 size_t alignment,
 					 gfp_t gfp);
+extern int spi_merge_transfers(struct spi_master *master,
+			       struct spi_message *msg,
+			       size_t min_size,
+			       size_t max_size,
+			       gfp_t gfp);
 
 /*---------------------------------------------------------------------------*/
 
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 6/8] spi: core: add spi_master.translate_message
       [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
                     ` (4 preceding siblings ...)
  2015-12-14 15:20   ` [PATCH v3 5/8] spi: core: add spi_merge_transfers method kernel-TqfNSX0MhmxHKSADF0wUEw
@ 2015-12-14 15:20   ` kernel-TqfNSX0MhmxHKSADF0wUEw
  2015-12-14 15:20   ` [PATCH v3 7/8] spi: core: add spi_master.min_dma_len and supporting methods kernel-TqfNSX0MhmxHKSADF0wUEw
  2015-12-14 15:20   ` [PATCH v3 8/8] spi: bcm2835: move to spi-core methods translate_message and can_dma kernel-TqfNSX0MhmxHKSADF0wUEw
  7 siblings, 0 replies; 11+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-12-14 15:20 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Martin Sperl

From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

Add translate_message to spi_master structure to allow
translations of spi_messages to happen independently of
prepare messages.

This "translations" are supposed to be using spi_res to handle the
reverse transformation (see spi_replace_transfers), so no explicit
code is necessary to revert any changes.

dma_mapping of spi_messages could also become part of this processing.

The long-term view is that this translation as well as the
dma_mapping of the messages may happen in a separate thread
when we are queueing those spi_messages - this would allows
for better use of cpu resources as well as reduction of latency
in the case of queuing.

Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
 drivers/spi/spi.c       |   15 +++++++++++++++
 include/linux/spi/spi.h |    4 ++++
 2 files changed, 19 insertions(+)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index fe3b18e..020e34d 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1170,6 +1170,21 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)

 	trace_spi_message_start(master->cur_msg);

+	/* under some circumstances (i.e: when queuing a message) this
+	 * could get moved to a separate thread to reduce latencies
+	 * this could also handle the mapping of spi_transfers
+	 */
+	if (master->translate_message) {
+		ret = master->translate_message(master, master->cur_msg);
+		if (ret) {
+			dev_err(&master->dev,
+				"failed to translate message: %d\n", ret);
+			master->cur_msg->status = ret;
+			spi_finalize_current_message(master);
+			return;
+		}
+	}
+
 	if (master->prepare_message) {
 		ret = master->prepare_message(master, master->cur_msg);
 		if (ret) {
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 7172e73..4b4c1e9 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -379,6 +379,8 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  * @handle_err: the subsystem calls the driver to handle an error that occurs
  *		in the generic implementation of transfer_one_message().
  * @unprepare_message: undo any work done by prepare_message().
+ * @translate_message: apply some message translations to optimize for the
+ *                     spi_master
  * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
  *	number. Any individual value may be -ENOENT for CS lines that
  *	are not GPIOs (driven by the SPI controller itself).
@@ -525,6 +527,8 @@ struct spi_master {
 			       struct spi_message *message);
 	int (*unprepare_message)(struct spi_master *master,
 				 struct spi_message *message);
+	int (*translate_message)(struct spi_master *master,
+				 struct spi_message *message);

 	/*
 	 * These hooks are for drivers that use a generic implementation
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 7/8] spi: core: add spi_master.min_dma_len and supporting methods
       [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
                     ` (5 preceding siblings ...)
  2015-12-14 15:20   ` [PATCH v3 6/8] spi: core: add spi_master.translate_message kernel-TqfNSX0MhmxHKSADF0wUEw
@ 2015-12-14 15:20   ` kernel-TqfNSX0MhmxHKSADF0wUEw
  2015-12-14 15:20   ` [PATCH v3 8/8] spi: bcm2835: move to spi-core methods translate_message and can_dma kernel-TqfNSX0MhmxHKSADF0wUEw
  7 siblings, 0 replies; 11+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-12-14 15:20 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Martin Sperl

From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

Added spi_master.min_dma_len plus methods requireing this information:
* spi_translate_message_size_align_merge
* spi_can_dma_min_dma_len

Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
 drivers/spi/spi.c       |   66 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/spi/spi.h |   14 +++++++++-
 2 files changed, 79 insertions(+), 1 deletion(-)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 020e34d..883bfa8 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -277,6 +277,27 @@ void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
 }
 EXPORT_SYMBOL_GPL(spi_statistics_add_transfer_stats);

+/**
+ * spi_can_dma_min_dma_len - default implementation for spi_can_dma
+ *                           that only checks spi_transfer.len is bigger
+ *                           spi_master.min_dma_len
+ * @master: the spi_master device
+ * @spi:    the spi_device
+ * @tfr:    the spi_transfer
+ */
+bool spi_can_dma_min_dma_len(struct spi_master *master,
+			     struct spi_device *spi,
+			     struct spi_transfer *tfr)
+{
+	/* we start DMA efforts only on bigger transfers */
+	if (tfr->len < master->min_dma_len)
+		return false;
+
+	/* return OK */
+	return true;
+}
+EXPORT_SYMBOL_GPL(spi_can_dma_min_dma_len);
+
 /* modalias support makes "modprobe $MODALIAS" new-style hotplug work,
  * and the sysfs version makes coldplug work too.
  */
@@ -2793,6 +2814,51 @@ int spi_merge_transfers(struct spi_master *master,
 	return 0;
 }
 EXPORT_SYMBOL_GPL(spi_merge_transfers);
+/*-------------------------------------------------------------------------*/
+
+/**
+ * spi_translate_message_size_align_merge - default spi_message translation
+ *                                          code that takes its parameters
+ *                                          from @spi_master
+ *
+ * @master:  the spi_master for which we run this translation
+ * @message: the spi_message which we need to translate
+ *
+ * Returns: status of tranformation
+ */
+int spi_translate_message_size_align_merge(
+	struct spi_master *master, struct spi_message *message)
+{
+	int ret;
+
+	/* translate the message */
+
+	/* fix alignment of transfers by splitting rx_buf/tx_buf
+	 * (and worsted case copying tx_buf)
+	 */
+	ret = spi_split_transfers_unaligned(master, message,
+					    master->min_dma_len,
+					    master->dma_alignment,
+					    GFP_KERNEL);
+	if (ret)
+		return ret;
+
+	/* limit transfer length */
+	if (master->max_dma_len) {
+		ret = spi_split_transfers_maxsize(master, message,
+						  master->max_dma_len,
+						  GFP_KERNEL);
+		if (ret)
+			return ret;
+	}
+
+	/* merge spi_transfers up to a full page */
+	ret = spi_merge_transfers(master, message, 2, PAGE_SIZE,
+				  GFP_KERNEL);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(spi_translate_message_size_align_merge);

 /*-------------------------------------------------------------------------*/

diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 4b4c1e9..f055a47 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -351,6 +351,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
  *                   while the hardware is prepared, using the parent
  *                   device for the spidev
  * @max_dma_len: Maximum length of a DMA transfer for the device.
+ * @min_dma_len: Minimum length of a DMA transfer for the device.
+ *               (mostly to avoid dma_mapping a buffer when dma is not used,
+ *                should be multiple of dma_alignment)
  * @prepare_transfer_hardware: a message will soon arrive from the queue
  *	so the subsystem requests the driver to prepare the transfer hardware
  *	by issuing this call
@@ -423,7 +426,6 @@ struct spi_master {
 	 * buffers; let protocol drivers know about these requirements.
 	 */
 	u16			dma_alignment;
-
 	/* spi_device.mode flags understood by this controller driver */
 	u16			mode_bits;

@@ -517,6 +519,7 @@ struct spi_master {
 	bool                            cur_msg_prepared;
 	bool				cur_msg_mapped;
 	struct completion               xfer_completion;
+	size_t				min_dma_len;
 	size_t				max_dma_len;

 	int (*prepare_transfer_hardware)(struct spi_master *master);
@@ -940,6 +943,15 @@ extern struct spi_replaced_transfers *spi_replace_transfers(
 	size_t extradatasize,
 	gfp_t gfp);

+/* some default implementations that drivers may use */
+extern int spi_translate_message_size_align_merge(
+	struct spi_master *master, struct spi_message *message);
+
+/* a default implementation of can_dma */
+extern bool spi_can_dma_min_dma_len(struct spi_master *master,
+				    struct spi_device *spi,
+				    struct spi_transfer *tfr);
+
 /*---------------------------------------------------------------------------*/

 /* SPI transfer transformation methods */
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v3 8/8] spi: bcm2835: move to spi-core methods translate_message and can_dma
       [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
                     ` (6 preceding siblings ...)
  2015-12-14 15:20   ` [PATCH v3 7/8] spi: core: add spi_master.min_dma_len and supporting methods kernel-TqfNSX0MhmxHKSADF0wUEw
@ 2015-12-14 15:20   ` kernel-TqfNSX0MhmxHKSADF0wUEw
  7 siblings, 0 replies; 11+ messages in thread
From: kernel-TqfNSX0MhmxHKSADF0wUEw @ 2015-12-14 15:20 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Martin Sperl

From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>

Remove all limitations in can_dma that inhibit the use of DMA

To meet the limitiations of the HW we are now using the standardized
version of spi_translate_message, that:
* splits long transfers (> 60k)
* aligns transfers (on 4 byte boundary)
* merges transfers (for optimizations)

Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
 drivers/spi/spi-bcm2835.c |   58 +++++++++++++++------------------------------
 1 file changed, 19 insertions(+), 39 deletions(-)

Changelog:
  V1 -> V3: move to use spi-core methods where possible

diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c
index cf04960..75e4425 100644
--- a/drivers/spi/spi-bcm2835.c
+++ b/drivers/spi/spi-bcm2835.c
@@ -361,44 +361,8 @@ static bool bcm2835_spi_can_dma(struct spi_master *master,
 	if (!gpio_is_valid(spi->cs_gpio))
 		return false;

-	/* we start DMA efforts only on bigger transfers */
-	if (tfr->len < BCM2835_SPI_DMA_MIN_LENGTH)
-		return false;
-
-	/* BCM2835_SPI_DLEN has defined a max transfer size as
-	 * 16 bit, so max is 65535
-	 * we can revisit this by using an alternative transfer
-	 * method - ideally this would get done without any more
-	 * interaction...
-	 */
-	if (tfr->len > 65535) {
-		dev_warn_once(&spi->dev,
-			      "transfer size of %d too big for dma-transfer\n",
-			      tfr->len);
-		return false;
-	}
-
-	/* if we run rx/tx_buf with word aligned addresses then we are OK */
-	if ((((size_t)tfr->rx_buf & 3) == 0) &&
-	    (((size_t)tfr->tx_buf & 3) == 0))
-		return true;
-
-	/* otherwise we only allow transfers within the same page
-	 * to avoid wasting time on dma_mapping when it is not practical
-	 */
-	if (((size_t)tfr->tx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) {
-		dev_warn_once(&spi->dev,
-			      "Unaligned spi tx-transfer bridging page\n");
-		return false;
-	}
-	if (((size_t)tfr->rx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) {
-		dev_warn_once(&spi->dev,
-			      "Unaligned spi rx-transfer bridging page\n");
-		return false;
-	}
-
-	/* return OK */
-	return true;
+	/* use the default implementation */
+	return spi_can_dma_min_dma_len(master, spi, tfr);
 }

 static void bcm2835_dma_release(struct spi_master *master)
@@ -461,7 +425,23 @@ static void bcm2835_dma_init(struct spi_master *master, struct device *dev)

 	/* all went well, so set can_dma */
 	master->can_dma = bcm2835_spi_can_dma;
-	master->max_dma_len = 65535; /* limitation by BCM2835_SPI_DLEN */
+
+	/* set up transform message using the default implementation
+	 * for size, alignment and merging transfers
+	 */
+	master->translate_message = spi_translate_message_size_align_merge;
+
+	/* the minimum length when we run DMA */
+	master->min_dma_len = BCM2835_SPI_DMA_MIN_LENGTH;
+
+	/* the max_dma_len limited by BCM2835_SPI_DLEN is actually 65535,
+	 * but for al practical purposes we use 15 pages (60k)
+	 */
+	master->max_dma_len = 15 * PAGE_SIZE;
+
+	/* dma alignment is 4 bytes */
+	master->dma_alignment = 4;
+
 	/* need to do TX AND RX DMA, so we need dummy buffers */
 	master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;

--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v3 4/8] spi: core: added spi_split_transfers_unaligned
       [not found]     ` <1450106426-2277-5-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
@ 2015-12-16 16:11       ` Martin Sperl
  0 siblings, 0 replies; 11+ messages in thread
From: Martin Sperl @ 2015-12-16 16:11 UTC (permalink / raw)
  To: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt,
	linux-spi-u79uwXL29TY76Z2rM5mHXA,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r


> On 14.12.2015, at 16:20, kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org wrote:
> +	/* the number of bytes by which we need to shift to align */
> +	alignment_shift = BIT(alignment) - alignment_to_correct;

This actually needs to be:
	alignment_shift = alignment - alignment_to_correct

> +
> +	/* calculate the values */
> +	alignment_mask = (1 << alignment) - 1;
This actually needs to be:
	 alignment_mask = alignment - 1

This was due to a misconception of @spi_master.dma_alignment...

I will wait for other feedback before creating v4 of the
patchset.

Martin
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v3 3/8] spi: core: add spi_split_transfers_maxsize
       [not found]     ` <1450106426-2277-4-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
@ 2016-02-16  8:52       ` Geert Uytterhoeven
  0 siblings, 0 replies; 11+ messages in thread
From: Geert Uytterhoeven @ 2016-02-16  8:52 UTC (permalink / raw)
  To: Martin Sperl
  Cc: Mark Brown, Stephen Warren, Lee Jones, Eric Anholt, linux-spi,
	linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Mon, Dec 14, 2015 at 4:20 PM,  <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org> wrote:
> +int __spi_split_transfer_maxsize(struct spi_master *master,
> +                                struct spi_message *msg,
> +                                struct spi_transfer **xferp,
> +                                size_t maxsize,
> +                                gfp_t gfp)
> +{
> +       struct spi_transfer *xfer = *xferp, *xfers;
> +       struct spi_replaced_transfers *srt;
> +       size_t offset;
> +       size_t count, i;
> +
> +       /* warn once about this fact that we are splitting a transfer */
> +       dev_warn_once(&msg->spi->dev,
> +                     "spi_transfer of length %i exceed max length of %i - needed to split transfers\n",

On arm64:

drivers/spi/spi.c:2278:2: warning: format '%i' expects argument of
type 'int', but argument 4 has type 'size_t' [-Wformat=]
  dev_warn_once(&msg->spi->dev,

%u ... %zu


> +                     xfer->len, maxsize);
> +
> +       /* calculate how many we have to replace */
> +       count = DIV_ROUND_UP(xfer->len, maxsize);
> +
> +       /* create replacement */
> +       srt = spi_replace_transfers(msg, xfer, 1, count, NULL, 0, gfp);
> +       if (!srt)
> +               return -ENOMEM;
> +       xfers = srt->inserted_transfers;
> +
> +       /* now handle each of those newly inserted spi_transfers
> +        * note that the replacements spi_transfers all are preset
> +        * to the same values as *xferp, so tx_buf, rx_buf and len
> +        * are all identical (as well as most others)
> +        * so we just have to fix up len and the pointers.
> +        *
> +        * this also includes support for the depreciated
> +        * spi_message.is_dma_mapped interface
> +        */
> +
> +       /* the first transfer just needs the length modified, so we
> +        * run it outside the loop
> +        */
> +       xfers[0].len = min(maxsize, xfer[0].len);
> +
> +       /* all the others need rx_buf/tx_buf also set */
> +       for (i = 1, offset = maxsize; i < count; offset += maxsize, i++) {
> +               /* update rx_buf, tx_buf and dma */
> +               if (xfers[i].rx_buf)
> +                       xfers[i].rx_buf += offset;
> +               if (xfers[i].rx_dma)
> +                       xfers[i].rx_dma += offset;
> +               if (xfers[i].tx_buf)
> +                       xfers[i].tx_buf += offset;
> +               if (xfers[i].tx_dma)
> +                       xfers[i].tx_dma += offset;
> +
> +               /* update length */
> +               xfers[i].len = min(maxsize, xfers[i].len - offset);

On arm64:

drivers/spi/spi.c:2304:113: warning: comparison of distinct pointer
types lacks a cast

> +       }

Does it make sense that maxsize is size_t (32 or 64 bit), while
spi_transfer.len is
unsigned int (always 32 bit)?

I see this is coming from spi_master.max_dma_len, which is also size_t.

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert-Td1EMuHUCqxL1ZNQvxDV9g@public.gmane.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2016-02-16  8:52 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-12-14 15:20 [PATCH v3 0/8] spi: spi-message transformation framework kernel-TqfNSX0MhmxHKSADF0wUEw
     [not found] ` <1450106426-2277-1-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2015-12-14 15:20   ` [PATCH v3 1/8] spi: core: added spi_resource management kernel-TqfNSX0MhmxHKSADF0wUEw
2015-12-14 15:20   ` [PATCH v3 2/8] spi: core: add spi_replace_transfers method kernel-TqfNSX0MhmxHKSADF0wUEw
2015-12-14 15:20   ` [PATCH v3 3/8] spi: core: add spi_split_transfers_maxsize kernel-TqfNSX0MhmxHKSADF0wUEw
     [not found]     ` <1450106426-2277-4-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2016-02-16  8:52       ` Geert Uytterhoeven
2015-12-14 15:20   ` [PATCH v3 4/8] spi: core: added spi_split_transfers_unaligned kernel-TqfNSX0MhmxHKSADF0wUEw
     [not found]     ` <1450106426-2277-5-git-send-email-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2015-12-16 16:11       ` Martin Sperl
2015-12-14 15:20   ` [PATCH v3 5/8] spi: core: add spi_merge_transfers method kernel-TqfNSX0MhmxHKSADF0wUEw
2015-12-14 15:20   ` [PATCH v3 6/8] spi: core: add spi_master.translate_message kernel-TqfNSX0MhmxHKSADF0wUEw
2015-12-14 15:20   ` [PATCH v3 7/8] spi: core: add spi_master.min_dma_len and supporting methods kernel-TqfNSX0MhmxHKSADF0wUEw
2015-12-14 15:20   ` [PATCH v3 8/8] spi: bcm2835: move to spi-core methods translate_message and can_dma kernel-TqfNSX0MhmxHKSADF0wUEw

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.