xref: /openbmc/linux/drivers/infiniband/hw/hfi1/pio_copy.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1145eba1aSCai Huoqing // SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
2f48ad614SDennis Dalessandro /*
3f48ad614SDennis Dalessandro  * Copyright(c) 2015, 2016 Intel Corporation.
4f48ad614SDennis Dalessandro  */
5f48ad614SDennis Dalessandro 
6f48ad614SDennis Dalessandro #include "hfi.h"
7f48ad614SDennis Dalessandro 
8f48ad614SDennis Dalessandro /* additive distance between non-SOP and SOP space */
9f48ad614SDennis Dalessandro #define SOP_DISTANCE (TXE_PIO_SIZE / 2)
10f48ad614SDennis Dalessandro #define PIO_BLOCK_MASK (PIO_BLOCK_SIZE - 1)
11f48ad614SDennis Dalessandro /* number of QUADWORDs in a block */
12f48ad614SDennis Dalessandro #define PIO_BLOCK_QWS (PIO_BLOCK_SIZE / sizeof(u64))
13f48ad614SDennis Dalessandro 
14f48ad614SDennis Dalessandro /**
15f48ad614SDennis Dalessandro  * pio_copy - copy data block to MMIO space
16f4f86690SLee Jones  * @dd: hfi1 dev data
17f48ad614SDennis Dalessandro  * @pbuf: a number of blocks allocated within a PIO send context
18f48ad614SDennis Dalessandro  * @pbc: PBC to send
19f48ad614SDennis Dalessandro  * @from: source, must be 8 byte aligned
20f48ad614SDennis Dalessandro  * @count: number of DWORD (32-bit) quantities to copy from source
21f48ad614SDennis Dalessandro  *
22f48ad614SDennis Dalessandro  * Copy data from source to PIO Send Buffer memory, 8 bytes at a time.
23f48ad614SDennis Dalessandro  * Must always write full BLOCK_SIZE bytes blocks.  The first block must
24f48ad614SDennis Dalessandro  * be written to the corresponding SOP=1 address.
25f48ad614SDennis Dalessandro  *
26f48ad614SDennis Dalessandro  * Known:
27f48ad614SDennis Dalessandro  * o pbuf->start always starts on a block boundary
28f48ad614SDennis Dalessandro  * o pbuf can wrap only at a block boundary
29f48ad614SDennis Dalessandro  */
pio_copy(struct hfi1_devdata * dd,struct pio_buf * pbuf,u64 pbc,const void * from,size_t count)30f48ad614SDennis Dalessandro void pio_copy(struct hfi1_devdata *dd, struct pio_buf *pbuf, u64 pbc,
31f48ad614SDennis Dalessandro 	      const void *from, size_t count)
32f48ad614SDennis Dalessandro {
33f48ad614SDennis Dalessandro 	void __iomem *dest = pbuf->start + SOP_DISTANCE;
34f48ad614SDennis Dalessandro 	void __iomem *send = dest + PIO_BLOCK_SIZE;
35f48ad614SDennis Dalessandro 	void __iomem *dend;			/* 8-byte data end */
36f48ad614SDennis Dalessandro 
37f48ad614SDennis Dalessandro 	/* write the PBC */
38f48ad614SDennis Dalessandro 	writeq(pbc, dest);
39f48ad614SDennis Dalessandro 	dest += sizeof(u64);
40f48ad614SDennis Dalessandro 
41f48ad614SDennis Dalessandro 	/* calculate where the QWORD data ends - in SOP=1 space */
42f48ad614SDennis Dalessandro 	dend = dest + ((count >> 1) * sizeof(u64));
43f48ad614SDennis Dalessandro 
44f48ad614SDennis Dalessandro 	if (dend < send) {
45f48ad614SDennis Dalessandro 		/*
46f48ad614SDennis Dalessandro 		 * all QWORD data is within the SOP block, does *not*
47f48ad614SDennis Dalessandro 		 * reach the end of the SOP block
48f48ad614SDennis Dalessandro 		 */
49f48ad614SDennis Dalessandro 
50f48ad614SDennis Dalessandro 		while (dest < dend) {
51f48ad614SDennis Dalessandro 			writeq(*(u64 *)from, dest);
52f48ad614SDennis Dalessandro 			from += sizeof(u64);
53f48ad614SDennis Dalessandro 			dest += sizeof(u64);
54f48ad614SDennis Dalessandro 		}
55f48ad614SDennis Dalessandro 		/*
56f48ad614SDennis Dalessandro 		 * No boundary checks are needed here:
57f48ad614SDennis Dalessandro 		 * 0. We're not on the SOP block boundary
58f48ad614SDennis Dalessandro 		 * 1. The possible DWORD dangle will still be within
59f48ad614SDennis Dalessandro 		 *    the SOP block
60f48ad614SDennis Dalessandro 		 * 2. We cannot wrap except on a block boundary.
61f48ad614SDennis Dalessandro 		 */
62f48ad614SDennis Dalessandro 	} else {
63f48ad614SDennis Dalessandro 		/* QWORD data extends _to_ or beyond the SOP block */
64f48ad614SDennis Dalessandro 
65f48ad614SDennis Dalessandro 		/* write 8-byte SOP chunk data */
66f48ad614SDennis Dalessandro 		while (dest < send) {
67f48ad614SDennis Dalessandro 			writeq(*(u64 *)from, dest);
68f48ad614SDennis Dalessandro 			from += sizeof(u64);
69f48ad614SDennis Dalessandro 			dest += sizeof(u64);
70f48ad614SDennis Dalessandro 		}
71f48ad614SDennis Dalessandro 		/* drop out of the SOP range */
72f48ad614SDennis Dalessandro 		dest -= SOP_DISTANCE;
73f48ad614SDennis Dalessandro 		dend -= SOP_DISTANCE;
74f48ad614SDennis Dalessandro 
75f48ad614SDennis Dalessandro 		/*
76f48ad614SDennis Dalessandro 		 * If the wrap comes before or matches the data end,
77f48ad614SDennis Dalessandro 		 * copy until until the wrap, then wrap.
78f48ad614SDennis Dalessandro 		 *
79f48ad614SDennis Dalessandro 		 * If the data ends at the end of the SOP above and
80f48ad614SDennis Dalessandro 		 * the buffer wraps, then pbuf->end == dend == dest
81f48ad614SDennis Dalessandro 		 * and nothing will get written, but we will wrap in
82f48ad614SDennis Dalessandro 		 * case there is a dangling DWORD.
83f48ad614SDennis Dalessandro 		 */
84f48ad614SDennis Dalessandro 		if (pbuf->end <= dend) {
85f48ad614SDennis Dalessandro 			while (dest < pbuf->end) {
86f48ad614SDennis Dalessandro 				writeq(*(u64 *)from, dest);
87f48ad614SDennis Dalessandro 				from += sizeof(u64);
88f48ad614SDennis Dalessandro 				dest += sizeof(u64);
89f48ad614SDennis Dalessandro 			}
90f48ad614SDennis Dalessandro 
918af8d297SSebastian Sanchez 			dest -= pbuf->sc->size;
928af8d297SSebastian Sanchez 			dend -= pbuf->sc->size;
93f48ad614SDennis Dalessandro 		}
94f48ad614SDennis Dalessandro 
95f48ad614SDennis Dalessandro 		/* write 8-byte non-SOP, non-wrap chunk data */
96f48ad614SDennis Dalessandro 		while (dest < dend) {
97f48ad614SDennis Dalessandro 			writeq(*(u64 *)from, dest);
98f48ad614SDennis Dalessandro 			from += sizeof(u64);
99f48ad614SDennis Dalessandro 			dest += sizeof(u64);
100f48ad614SDennis Dalessandro 		}
101f48ad614SDennis Dalessandro 	}
102f48ad614SDennis Dalessandro 	/* at this point we have wrapped if we are going to wrap */
103f48ad614SDennis Dalessandro 
104f48ad614SDennis Dalessandro 	/* write dangling u32, if any */
105f48ad614SDennis Dalessandro 	if (count & 1) {
106f48ad614SDennis Dalessandro 		union mix val;
107f48ad614SDennis Dalessandro 
108f48ad614SDennis Dalessandro 		val.val64 = 0;
109f48ad614SDennis Dalessandro 		val.val32[0] = *(u32 *)from;
110f48ad614SDennis Dalessandro 		writeq(val.val64, dest);
111f48ad614SDennis Dalessandro 		dest += sizeof(u64);
112f48ad614SDennis Dalessandro 	}
113f48ad614SDennis Dalessandro 	/*
114f48ad614SDennis Dalessandro 	 * fill in rest of block, no need to check pbuf->end
115f48ad614SDennis Dalessandro 	 * as we only wrap on a block boundary
116f48ad614SDennis Dalessandro 	 */
117f48ad614SDennis Dalessandro 	while (((unsigned long)dest & PIO_BLOCK_MASK) != 0) {
118f48ad614SDennis Dalessandro 		writeq(0, dest);
119f48ad614SDennis Dalessandro 		dest += sizeof(u64);
120f48ad614SDennis Dalessandro 	}
121f48ad614SDennis Dalessandro 
122f48ad614SDennis Dalessandro 	/* finished with this buffer */
123f48ad614SDennis Dalessandro 	this_cpu_dec(*pbuf->sc->buffers_allocated);
124f48ad614SDennis Dalessandro 	preempt_enable();
125f48ad614SDennis Dalessandro }
126f48ad614SDennis Dalessandro 
127f48ad614SDennis Dalessandro /*
128f48ad614SDennis Dalessandro  * Handle carry bytes using shifts and masks.
129f48ad614SDennis Dalessandro  *
130f48ad614SDennis Dalessandro  * NOTE: the value the unused portion of carry is expected to always be zero.
131f48ad614SDennis Dalessandro  */
132f48ad614SDennis Dalessandro 
133f48ad614SDennis Dalessandro /*
134f48ad614SDennis Dalessandro  * "zero" shift - bit shift used to zero out upper bytes.  Input is
135f48ad614SDennis Dalessandro  * the count of LSB bytes to preserve.
136f48ad614SDennis Dalessandro  */
137f48ad614SDennis Dalessandro #define zshift(x) (8 * (8 - (x)))
138f48ad614SDennis Dalessandro 
139f48ad614SDennis Dalessandro /*
140f48ad614SDennis Dalessandro  * "merge" shift - bit shift used to merge with carry bytes.  Input is
141f48ad614SDennis Dalessandro  * the LSB byte count to move beyond.
142f48ad614SDennis Dalessandro  */
143f48ad614SDennis Dalessandro #define mshift(x) (8 * (x))
144f48ad614SDennis Dalessandro 
145f48ad614SDennis Dalessandro /*
14661868fb5SSebastian Sanchez  * Jump copy - no-loop copy for < 8 bytes.
14761868fb5SSebastian Sanchez  */
jcopy(u8 * dest,const u8 * src,u32 n)14861868fb5SSebastian Sanchez static inline void jcopy(u8 *dest, const u8 *src, u32 n)
14961868fb5SSebastian Sanchez {
15061868fb5SSebastian Sanchez 	switch (n) {
15161868fb5SSebastian Sanchez 	case 7:
15261868fb5SSebastian Sanchez 		*dest++ = *src++;
1536f24b159SGustavo A. R. Silva 		fallthrough;
15461868fb5SSebastian Sanchez 	case 6:
15561868fb5SSebastian Sanchez 		*dest++ = *src++;
1566f24b159SGustavo A. R. Silva 		fallthrough;
15761868fb5SSebastian Sanchez 	case 5:
15861868fb5SSebastian Sanchez 		*dest++ = *src++;
1596f24b159SGustavo A. R. Silva 		fallthrough;
16061868fb5SSebastian Sanchez 	case 4:
16161868fb5SSebastian Sanchez 		*dest++ = *src++;
1626f24b159SGustavo A. R. Silva 		fallthrough;
16361868fb5SSebastian Sanchez 	case 3:
16461868fb5SSebastian Sanchez 		*dest++ = *src++;
1656f24b159SGustavo A. R. Silva 		fallthrough;
16661868fb5SSebastian Sanchez 	case 2:
16761868fb5SSebastian Sanchez 		*dest++ = *src++;
1686f24b159SGustavo A. R. Silva 		fallthrough;
16961868fb5SSebastian Sanchez 	case 1:
17061868fb5SSebastian Sanchez 		*dest++ = *src++;
17161868fb5SSebastian Sanchez 	}
17261868fb5SSebastian Sanchez }
17361868fb5SSebastian Sanchez 
17461868fb5SSebastian Sanchez /*
175*650126a8SXiang wangx  * Read nbytes from "from" and place them in the low bytes
17661868fb5SSebastian Sanchez  * of pbuf->carry.  Other bytes are left as-is.  Any previous
17761868fb5SSebastian Sanchez  * value in pbuf->carry is lost.
178f48ad614SDennis Dalessandro  *
179f48ad614SDennis Dalessandro  * NOTES:
180f48ad614SDennis Dalessandro  * o do not read from from if nbytes is zero
18161868fb5SSebastian Sanchez  * o from may _not_ be u64 aligned.
182f48ad614SDennis Dalessandro  */
read_low_bytes(struct pio_buf * pbuf,const void * from,unsigned int nbytes)183f48ad614SDennis Dalessandro static inline void read_low_bytes(struct pio_buf *pbuf, const void *from,
184f48ad614SDennis Dalessandro 				  unsigned int nbytes)
185f48ad614SDennis Dalessandro {
186f48ad614SDennis Dalessandro 	pbuf->carry.val64 = 0;
18761868fb5SSebastian Sanchez 	jcopy(&pbuf->carry.val8[0], from, nbytes);
188f48ad614SDennis Dalessandro 	pbuf->carry_bytes = nbytes;
189f48ad614SDennis Dalessandro }
190f48ad614SDennis Dalessandro 
191f48ad614SDennis Dalessandro /*
19261868fb5SSebastian Sanchez  * Read nbytes bytes from "from" and put them at the end of pbuf->carry.
19361868fb5SSebastian Sanchez  * It is expected that the extra read does not overfill carry.
194f48ad614SDennis Dalessandro  *
195f48ad614SDennis Dalessandro  * NOTES:
196f48ad614SDennis Dalessandro  * o from may _not_ be u64 aligned
197f48ad614SDennis Dalessandro  * o nbytes may span a QW boundary
198f48ad614SDennis Dalessandro  */
read_extra_bytes(struct pio_buf * pbuf,const void * from,unsigned int nbytes)199f48ad614SDennis Dalessandro static inline void read_extra_bytes(struct pio_buf *pbuf,
200f48ad614SDennis Dalessandro 				    const void *from, unsigned int nbytes)
201f48ad614SDennis Dalessandro {
20261868fb5SSebastian Sanchez 	jcopy(&pbuf->carry.val8[pbuf->carry_bytes], from, nbytes);
20361868fb5SSebastian Sanchez 	pbuf->carry_bytes += nbytes;
204f48ad614SDennis Dalessandro }
205f48ad614SDennis Dalessandro 
206f48ad614SDennis Dalessandro /*
207f48ad614SDennis Dalessandro  * Write a quad word using parts of pbuf->carry and the next 8 bytes of src.
208f48ad614SDennis Dalessandro  * Put the unused part of the next 8 bytes of src into the LSB bytes of
209f48ad614SDennis Dalessandro  * pbuf->carry with the upper bytes zeroed..
210f48ad614SDennis Dalessandro  *
211f48ad614SDennis Dalessandro  * NOTES:
212f48ad614SDennis Dalessandro  * o result must keep unused bytes zeroed
213f48ad614SDennis Dalessandro  * o src must be u64 aligned
214f48ad614SDennis Dalessandro  */
merge_write8(struct pio_buf * pbuf,void __iomem * dest,const void * src)215f48ad614SDennis Dalessandro static inline void merge_write8(
216f48ad614SDennis Dalessandro 	struct pio_buf *pbuf,
217f48ad614SDennis Dalessandro 	void __iomem *dest,
218f48ad614SDennis Dalessandro 	const void *src)
219f48ad614SDennis Dalessandro {
220f48ad614SDennis Dalessandro 	u64 new, temp;
221f48ad614SDennis Dalessandro 
222f48ad614SDennis Dalessandro 	new = *(u64 *)src;
223f48ad614SDennis Dalessandro 	temp = pbuf->carry.val64 | (new << mshift(pbuf->carry_bytes));
224f48ad614SDennis Dalessandro 	writeq(temp, dest);
225f48ad614SDennis Dalessandro 	pbuf->carry.val64 = new >> zshift(pbuf->carry_bytes);
226f48ad614SDennis Dalessandro }
227f48ad614SDennis Dalessandro 
228f48ad614SDennis Dalessandro /*
229f48ad614SDennis Dalessandro  * Write a quad word using all bytes of carry.
230f48ad614SDennis Dalessandro  */
carry8_write8(union mix carry,void __iomem * dest)231f48ad614SDennis Dalessandro static inline void carry8_write8(union mix carry, void __iomem *dest)
232f48ad614SDennis Dalessandro {
233f48ad614SDennis Dalessandro 	writeq(carry.val64, dest);
234f48ad614SDennis Dalessandro }
235f48ad614SDennis Dalessandro 
236f48ad614SDennis Dalessandro /*
237f48ad614SDennis Dalessandro  * Write a quad word using all the valid bytes of carry.  If carry
238f48ad614SDennis Dalessandro  * has zero valid bytes, nothing is written.
239f48ad614SDennis Dalessandro  * Returns 0 on nothing written, non-zero on quad word written.
240f48ad614SDennis Dalessandro  */
carry_write8(struct pio_buf * pbuf,void __iomem * dest)241f48ad614SDennis Dalessandro static inline int carry_write8(struct pio_buf *pbuf, void __iomem *dest)
242f48ad614SDennis Dalessandro {
243f48ad614SDennis Dalessandro 	if (pbuf->carry_bytes) {
244f48ad614SDennis Dalessandro 		/* unused bytes are always kept zeroed, so just write */
245f48ad614SDennis Dalessandro 		writeq(pbuf->carry.val64, dest);
246f48ad614SDennis Dalessandro 		return 1;
247f48ad614SDennis Dalessandro 	}
248f48ad614SDennis Dalessandro 
249f48ad614SDennis Dalessandro 	return 0;
250f48ad614SDennis Dalessandro }
251f48ad614SDennis Dalessandro 
252f48ad614SDennis Dalessandro /*
253f48ad614SDennis Dalessandro  * Segmented PIO Copy - start
254f48ad614SDennis Dalessandro  *
255f48ad614SDennis Dalessandro  * Start a PIO copy.
256f48ad614SDennis Dalessandro  *
257f48ad614SDennis Dalessandro  * @pbuf: destination buffer
258f48ad614SDennis Dalessandro  * @pbc: the PBC for the PIO buffer
259f48ad614SDennis Dalessandro  * @from: data source, QWORD aligned
260f48ad614SDennis Dalessandro  * @nbytes: bytes to copy
261f48ad614SDennis Dalessandro  */
seg_pio_copy_start(struct pio_buf * pbuf,u64 pbc,const void * from,size_t nbytes)262f48ad614SDennis Dalessandro void seg_pio_copy_start(struct pio_buf *pbuf, u64 pbc,
263f48ad614SDennis Dalessandro 			const void *from, size_t nbytes)
264f48ad614SDennis Dalessandro {
265f48ad614SDennis Dalessandro 	void __iomem *dest = pbuf->start + SOP_DISTANCE;
266f48ad614SDennis Dalessandro 	void __iomem *send = dest + PIO_BLOCK_SIZE;
267f48ad614SDennis Dalessandro 	void __iomem *dend;			/* 8-byte data end */
268f48ad614SDennis Dalessandro 
269f48ad614SDennis Dalessandro 	writeq(pbc, dest);
270f48ad614SDennis Dalessandro 	dest += sizeof(u64);
271f48ad614SDennis Dalessandro 
272f48ad614SDennis Dalessandro 	/* calculate where the QWORD data ends - in SOP=1 space */
273f48ad614SDennis Dalessandro 	dend = dest + ((nbytes >> 3) * sizeof(u64));
274f48ad614SDennis Dalessandro 
275f48ad614SDennis Dalessandro 	if (dend < send) {
276f48ad614SDennis Dalessandro 		/*
277f48ad614SDennis Dalessandro 		 * all QWORD data is within the SOP block, does *not*
278f48ad614SDennis Dalessandro 		 * reach the end of the SOP block
279f48ad614SDennis Dalessandro 		 */
280f48ad614SDennis Dalessandro 
281f48ad614SDennis Dalessandro 		while (dest < dend) {
282f48ad614SDennis Dalessandro 			writeq(*(u64 *)from, dest);
283f48ad614SDennis Dalessandro 			from += sizeof(u64);
284f48ad614SDennis Dalessandro 			dest += sizeof(u64);
285f48ad614SDennis Dalessandro 		}
286f48ad614SDennis Dalessandro 		/*
287f48ad614SDennis Dalessandro 		 * No boundary checks are needed here:
288f48ad614SDennis Dalessandro 		 * 0. We're not on the SOP block boundary
289f48ad614SDennis Dalessandro 		 * 1. The possible DWORD dangle will still be within
290f48ad614SDennis Dalessandro 		 *    the SOP block
291f48ad614SDennis Dalessandro 		 * 2. We cannot wrap except on a block boundary.
292f48ad614SDennis Dalessandro 		 */
293f48ad614SDennis Dalessandro 	} else {
294f48ad614SDennis Dalessandro 		/* QWORD data extends _to_ or beyond the SOP block */
295f48ad614SDennis Dalessandro 
296f48ad614SDennis Dalessandro 		/* write 8-byte SOP chunk data */
297f48ad614SDennis Dalessandro 		while (dest < send) {
298f48ad614SDennis Dalessandro 			writeq(*(u64 *)from, dest);
299f48ad614SDennis Dalessandro 			from += sizeof(u64);
300f48ad614SDennis Dalessandro 			dest += sizeof(u64);
301f48ad614SDennis Dalessandro 		}
302f48ad614SDennis Dalessandro 		/* drop out of the SOP range */
303f48ad614SDennis Dalessandro 		dest -= SOP_DISTANCE;
304f48ad614SDennis Dalessandro 		dend -= SOP_DISTANCE;
305f48ad614SDennis Dalessandro 
306f48ad614SDennis Dalessandro 		/*
307f48ad614SDennis Dalessandro 		 * If the wrap comes before or matches the data end,
308f48ad614SDennis Dalessandro 		 * copy until until the wrap, then wrap.
309f48ad614SDennis Dalessandro 		 *
310f48ad614SDennis Dalessandro 		 * If the data ends at the end of the SOP above and
311f48ad614SDennis Dalessandro 		 * the buffer wraps, then pbuf->end == dend == dest
312f48ad614SDennis Dalessandro 		 * and nothing will get written, but we will wrap in
313f48ad614SDennis Dalessandro 		 * case there is a dangling DWORD.
314f48ad614SDennis Dalessandro 		 */
315f48ad614SDennis Dalessandro 		if (pbuf->end <= dend) {
316f48ad614SDennis Dalessandro 			while (dest < pbuf->end) {
317f48ad614SDennis Dalessandro 				writeq(*(u64 *)from, dest);
318f48ad614SDennis Dalessandro 				from += sizeof(u64);
319f48ad614SDennis Dalessandro 				dest += sizeof(u64);
320f48ad614SDennis Dalessandro 			}
321f48ad614SDennis Dalessandro 
3228af8d297SSebastian Sanchez 			dest -= pbuf->sc->size;
3238af8d297SSebastian Sanchez 			dend -= pbuf->sc->size;
324f48ad614SDennis Dalessandro 		}
325f48ad614SDennis Dalessandro 
326f48ad614SDennis Dalessandro 		/* write 8-byte non-SOP, non-wrap chunk data */
327f48ad614SDennis Dalessandro 		while (dest < dend) {
328f48ad614SDennis Dalessandro 			writeq(*(u64 *)from, dest);
329f48ad614SDennis Dalessandro 			from += sizeof(u64);
330f48ad614SDennis Dalessandro 			dest += sizeof(u64);
331f48ad614SDennis Dalessandro 		}
332f48ad614SDennis Dalessandro 	}
333f48ad614SDennis Dalessandro 	/* at this point we have wrapped if we are going to wrap */
334f48ad614SDennis Dalessandro 
335f48ad614SDennis Dalessandro 	/* ...but it doesn't matter as we're done writing */
336f48ad614SDennis Dalessandro 
337f48ad614SDennis Dalessandro 	/* save dangling bytes, if any */
338f48ad614SDennis Dalessandro 	read_low_bytes(pbuf, from, nbytes & 0x7);
339f48ad614SDennis Dalessandro 
340f48ad614SDennis Dalessandro 	pbuf->qw_written = 1 /*PBC*/ + (nbytes >> 3);
341f48ad614SDennis Dalessandro }
342f48ad614SDennis Dalessandro 
343f48ad614SDennis Dalessandro /*
344f48ad614SDennis Dalessandro  * Mid copy helper, "mixed case" - source is 64-bit aligned but carry
345f48ad614SDennis Dalessandro  * bytes are non-zero.
346f48ad614SDennis Dalessandro  *
347f48ad614SDennis Dalessandro  * Whole u64s must be written to the chip, so bytes must be manually merged.
348f48ad614SDennis Dalessandro  *
349f48ad614SDennis Dalessandro  * @pbuf: destination buffer
350f48ad614SDennis Dalessandro  * @from: data source, is QWORD aligned.
351f48ad614SDennis Dalessandro  * @nbytes: bytes to copy
352f48ad614SDennis Dalessandro  *
353f48ad614SDennis Dalessandro  * Must handle nbytes < 8.
354f48ad614SDennis Dalessandro  */
mid_copy_mix(struct pio_buf * pbuf,const void * from,size_t nbytes)355f48ad614SDennis Dalessandro static void mid_copy_mix(struct pio_buf *pbuf, const void *from, size_t nbytes)
356f48ad614SDennis Dalessandro {
357f48ad614SDennis Dalessandro 	void __iomem *dest = pbuf->start + (pbuf->qw_written * sizeof(u64));
358f48ad614SDennis Dalessandro 	void __iomem *dend;			/* 8-byte data end */
359a4309d94SSebastian Sanchez 	unsigned long qw_to_write = nbytes >> 3;
360a4309d94SSebastian Sanchez 	unsigned long bytes_left = nbytes & 0x7;
361f48ad614SDennis Dalessandro 
362f48ad614SDennis Dalessandro 	/* calculate 8-byte data end */
363f48ad614SDennis Dalessandro 	dend = dest + (qw_to_write * sizeof(u64));
364f48ad614SDennis Dalessandro 
365f48ad614SDennis Dalessandro 	if (pbuf->qw_written < PIO_BLOCK_QWS) {
366f48ad614SDennis Dalessandro 		/*
367f48ad614SDennis Dalessandro 		 * Still within SOP block.  We don't need to check for
368f48ad614SDennis Dalessandro 		 * wrap because we are still in the first block and
369f48ad614SDennis Dalessandro 		 * can only wrap on block boundaries.
370f48ad614SDennis Dalessandro 		 */
371f48ad614SDennis Dalessandro 		void __iomem *send;		/* SOP end */
372f48ad614SDennis Dalessandro 		void __iomem *xend;
373f48ad614SDennis Dalessandro 
374f48ad614SDennis Dalessandro 		/*
375f48ad614SDennis Dalessandro 		 * calculate the end of data or end of block, whichever
376f48ad614SDennis Dalessandro 		 * comes first
377f48ad614SDennis Dalessandro 		 */
378f48ad614SDennis Dalessandro 		send = pbuf->start + PIO_BLOCK_SIZE;
379f48ad614SDennis Dalessandro 		xend = min(send, dend);
380f48ad614SDennis Dalessandro 
381f48ad614SDennis Dalessandro 		/* shift up to SOP=1 space */
382f48ad614SDennis Dalessandro 		dest += SOP_DISTANCE;
383f48ad614SDennis Dalessandro 		xend += SOP_DISTANCE;
384f48ad614SDennis Dalessandro 
385f48ad614SDennis Dalessandro 		/* write 8-byte chunk data */
386f48ad614SDennis Dalessandro 		while (dest < xend) {
387f48ad614SDennis Dalessandro 			merge_write8(pbuf, dest, from);
388f48ad614SDennis Dalessandro 			from += sizeof(u64);
389f48ad614SDennis Dalessandro 			dest += sizeof(u64);
390f48ad614SDennis Dalessandro 		}
391f48ad614SDennis Dalessandro 
392f48ad614SDennis Dalessandro 		/* shift down to SOP=0 space */
393f48ad614SDennis Dalessandro 		dest -= SOP_DISTANCE;
394f48ad614SDennis Dalessandro 	}
395f48ad614SDennis Dalessandro 	/*
396f48ad614SDennis Dalessandro 	 * At this point dest could be (either, both, or neither):
397f48ad614SDennis Dalessandro 	 * - at dend
398f48ad614SDennis Dalessandro 	 * - at the wrap
399f48ad614SDennis Dalessandro 	 */
400f48ad614SDennis Dalessandro 
401f48ad614SDennis Dalessandro 	/*
402f48ad614SDennis Dalessandro 	 * If the wrap comes before or matches the data end,
403f48ad614SDennis Dalessandro 	 * copy until until the wrap, then wrap.
404f48ad614SDennis Dalessandro 	 *
405f48ad614SDennis Dalessandro 	 * If dest is at the wrap, we will fall into the if,
406f48ad614SDennis Dalessandro 	 * not do the loop, when wrap.
407f48ad614SDennis Dalessandro 	 *
408f48ad614SDennis Dalessandro 	 * If the data ends at the end of the SOP above and
409f48ad614SDennis Dalessandro 	 * the buffer wraps, then pbuf->end == dend == dest
410f48ad614SDennis Dalessandro 	 * and nothing will get written.
411f48ad614SDennis Dalessandro 	 */
412f48ad614SDennis Dalessandro 	if (pbuf->end <= dend) {
413f48ad614SDennis Dalessandro 		while (dest < pbuf->end) {
414f48ad614SDennis Dalessandro 			merge_write8(pbuf, dest, from);
415f48ad614SDennis Dalessandro 			from += sizeof(u64);
416f48ad614SDennis Dalessandro 			dest += sizeof(u64);
417f48ad614SDennis Dalessandro 		}
418f48ad614SDennis Dalessandro 
4198af8d297SSebastian Sanchez 		dest -= pbuf->sc->size;
4208af8d297SSebastian Sanchez 		dend -= pbuf->sc->size;
421f48ad614SDennis Dalessandro 	}
422f48ad614SDennis Dalessandro 
423f48ad614SDennis Dalessandro 	/* write 8-byte non-SOP, non-wrap chunk data */
424f48ad614SDennis Dalessandro 	while (dest < dend) {
425f48ad614SDennis Dalessandro 		merge_write8(pbuf, dest, from);
426f48ad614SDennis Dalessandro 		from += sizeof(u64);
427f48ad614SDennis Dalessandro 		dest += sizeof(u64);
428f48ad614SDennis Dalessandro 	}
429f48ad614SDennis Dalessandro 
430f48ad614SDennis Dalessandro 	pbuf->qw_written += qw_to_write;
431a4309d94SSebastian Sanchez 
432a4309d94SSebastian Sanchez 	/* handle carry and left-over bytes */
433a4309d94SSebastian Sanchez 	if (pbuf->carry_bytes + bytes_left >= 8) {
434a4309d94SSebastian Sanchez 		unsigned long nread;
435a4309d94SSebastian Sanchez 
436a4309d94SSebastian Sanchez 		/* there is enough to fill another qw - fill carry */
437a4309d94SSebastian Sanchez 		nread = 8 - pbuf->carry_bytes;
438a4309d94SSebastian Sanchez 		read_extra_bytes(pbuf, from, nread);
439a4309d94SSebastian Sanchez 
440a4309d94SSebastian Sanchez 		/*
441a4309d94SSebastian Sanchez 		 * One more write - but need to make sure dest is correct.
442a4309d94SSebastian Sanchez 		 * Check for wrap and the possibility the write
443a4309d94SSebastian Sanchez 		 * should be in SOP space.
444a4309d94SSebastian Sanchez 		 *
445a4309d94SSebastian Sanchez 		 * The two checks immediately below cannot both be true, hence
446a4309d94SSebastian Sanchez 		 * the else. If we have wrapped, we cannot still be within the
447a4309d94SSebastian Sanchez 		 * first block. Conversely, if we are still in the first block,
448a4309d94SSebastian Sanchez 		 * we cannot have wrapped. We do the wrap check first as that
449a4309d94SSebastian Sanchez 		 * is more likely.
450a4309d94SSebastian Sanchez 		 */
451a4309d94SSebastian Sanchez 		/* adjust if we have wrapped */
452a4309d94SSebastian Sanchez 		if (dest >= pbuf->end)
4538af8d297SSebastian Sanchez 			dest -= pbuf->sc->size;
454a4309d94SSebastian Sanchez 		/* jump to the SOP range if within the first block */
455a4309d94SSebastian Sanchez 		else if (pbuf->qw_written < PIO_BLOCK_QWS)
456a4309d94SSebastian Sanchez 			dest += SOP_DISTANCE;
457a4309d94SSebastian Sanchez 
458a4309d94SSebastian Sanchez 		/* flush out full carry */
459a4309d94SSebastian Sanchez 		carry8_write8(pbuf->carry, dest);
460a4309d94SSebastian Sanchez 		pbuf->qw_written++;
461a4309d94SSebastian Sanchez 
462a4309d94SSebastian Sanchez 		/* now adjust and read the rest of the bytes into carry */
463a4309d94SSebastian Sanchez 		bytes_left -= nread;
464a4309d94SSebastian Sanchez 		from += nread; /* from is now not aligned */
465a4309d94SSebastian Sanchez 		read_low_bytes(pbuf, from, bytes_left);
466a4309d94SSebastian Sanchez 	} else {
467a4309d94SSebastian Sanchez 		/* not enough to fill another qw, append the rest to carry */
468a4309d94SSebastian Sanchez 		read_extra_bytes(pbuf, from, bytes_left);
469a4309d94SSebastian Sanchez 	}
470f48ad614SDennis Dalessandro }
471f48ad614SDennis Dalessandro 
472f48ad614SDennis Dalessandro /*
473f48ad614SDennis Dalessandro  * Mid copy helper, "straight case" - source pointer is 64-bit aligned
474f48ad614SDennis Dalessandro  * with no carry bytes.
475f48ad614SDennis Dalessandro  *
476f48ad614SDennis Dalessandro  * @pbuf: destination buffer
477f48ad614SDennis Dalessandro  * @from: data source, is QWORD aligned
478f48ad614SDennis Dalessandro  * @nbytes: bytes to copy
479f48ad614SDennis Dalessandro  *
480f48ad614SDennis Dalessandro  * Must handle nbytes < 8.
481f48ad614SDennis Dalessandro  */
mid_copy_straight(struct pio_buf * pbuf,const void * from,size_t nbytes)482f48ad614SDennis Dalessandro static void mid_copy_straight(struct pio_buf *pbuf,
483f48ad614SDennis Dalessandro 			      const void *from, size_t nbytes)
484f48ad614SDennis Dalessandro {
485f48ad614SDennis Dalessandro 	void __iomem *dest = pbuf->start + (pbuf->qw_written * sizeof(u64));
486f48ad614SDennis Dalessandro 	void __iomem *dend;			/* 8-byte data end */
487f48ad614SDennis Dalessandro 
488f48ad614SDennis Dalessandro 	/* calculate 8-byte data end */
489f48ad614SDennis Dalessandro 	dend = dest + ((nbytes >> 3) * sizeof(u64));
490f48ad614SDennis Dalessandro 
491f48ad614SDennis Dalessandro 	if (pbuf->qw_written < PIO_BLOCK_QWS) {
492f48ad614SDennis Dalessandro 		/*
493f48ad614SDennis Dalessandro 		 * Still within SOP block.  We don't need to check for
494f48ad614SDennis Dalessandro 		 * wrap because we are still in the first block and
495f48ad614SDennis Dalessandro 		 * can only wrap on block boundaries.
496f48ad614SDennis Dalessandro 		 */
497f48ad614SDennis Dalessandro 		void __iomem *send;		/* SOP end */
498f48ad614SDennis Dalessandro 		void __iomem *xend;
499f48ad614SDennis Dalessandro 
500f48ad614SDennis Dalessandro 		/*
501f48ad614SDennis Dalessandro 		 * calculate the end of data or end of block, whichever
502f48ad614SDennis Dalessandro 		 * comes first
503f48ad614SDennis Dalessandro 		 */
504f48ad614SDennis Dalessandro 		send = pbuf->start + PIO_BLOCK_SIZE;
505f48ad614SDennis Dalessandro 		xend = min(send, dend);
506f48ad614SDennis Dalessandro 
507f48ad614SDennis Dalessandro 		/* shift up to SOP=1 space */
508f48ad614SDennis Dalessandro 		dest += SOP_DISTANCE;
509f48ad614SDennis Dalessandro 		xend += SOP_DISTANCE;
510f48ad614SDennis Dalessandro 
511f48ad614SDennis Dalessandro 		/* write 8-byte chunk data */
512f48ad614SDennis Dalessandro 		while (dest < xend) {
513f48ad614SDennis Dalessandro 			writeq(*(u64 *)from, dest);
514f48ad614SDennis Dalessandro 			from += sizeof(u64);
515f48ad614SDennis Dalessandro 			dest += sizeof(u64);
516f48ad614SDennis Dalessandro 		}
517f48ad614SDennis Dalessandro 
518f48ad614SDennis Dalessandro 		/* shift down to SOP=0 space */
519f48ad614SDennis Dalessandro 		dest -= SOP_DISTANCE;
520f48ad614SDennis Dalessandro 	}
521f48ad614SDennis Dalessandro 	/*
522f48ad614SDennis Dalessandro 	 * At this point dest could be (either, both, or neither):
523f48ad614SDennis Dalessandro 	 * - at dend
524f48ad614SDennis Dalessandro 	 * - at the wrap
525f48ad614SDennis Dalessandro 	 */
526f48ad614SDennis Dalessandro 
527f48ad614SDennis Dalessandro 	/*
528f48ad614SDennis Dalessandro 	 * If the wrap comes before or matches the data end,
529f48ad614SDennis Dalessandro 	 * copy until until the wrap, then wrap.
530f48ad614SDennis Dalessandro 	 *
531f48ad614SDennis Dalessandro 	 * If dest is at the wrap, we will fall into the if,
532f48ad614SDennis Dalessandro 	 * not do the loop, when wrap.
533f48ad614SDennis Dalessandro 	 *
534f48ad614SDennis Dalessandro 	 * If the data ends at the end of the SOP above and
535f48ad614SDennis Dalessandro 	 * the buffer wraps, then pbuf->end == dend == dest
536f48ad614SDennis Dalessandro 	 * and nothing will get written.
537f48ad614SDennis Dalessandro 	 */
538f48ad614SDennis Dalessandro 	if (pbuf->end <= dend) {
539f48ad614SDennis Dalessandro 		while (dest < pbuf->end) {
540f48ad614SDennis Dalessandro 			writeq(*(u64 *)from, dest);
541f48ad614SDennis Dalessandro 			from += sizeof(u64);
542f48ad614SDennis Dalessandro 			dest += sizeof(u64);
543f48ad614SDennis Dalessandro 		}
544f48ad614SDennis Dalessandro 
5458af8d297SSebastian Sanchez 		dest -= pbuf->sc->size;
5468af8d297SSebastian Sanchez 		dend -= pbuf->sc->size;
547f48ad614SDennis Dalessandro 	}
548f48ad614SDennis Dalessandro 
549f48ad614SDennis Dalessandro 	/* write 8-byte non-SOP, non-wrap chunk data */
550f48ad614SDennis Dalessandro 	while (dest < dend) {
551f48ad614SDennis Dalessandro 		writeq(*(u64 *)from, dest);
552f48ad614SDennis Dalessandro 		from += sizeof(u64);
553f48ad614SDennis Dalessandro 		dest += sizeof(u64);
554f48ad614SDennis Dalessandro 	}
555f48ad614SDennis Dalessandro 
556f48ad614SDennis Dalessandro 	/* we know carry_bytes was zero on entry to this routine */
557f48ad614SDennis Dalessandro 	read_low_bytes(pbuf, from, nbytes & 0x7);
558f48ad614SDennis Dalessandro 
559f48ad614SDennis Dalessandro 	pbuf->qw_written += nbytes >> 3;
560f48ad614SDennis Dalessandro }
561f48ad614SDennis Dalessandro 
562f48ad614SDennis Dalessandro /*
563f48ad614SDennis Dalessandro  * Segmented PIO Copy - middle
564f48ad614SDennis Dalessandro  *
565f48ad614SDennis Dalessandro  * Must handle any aligned tail and any aligned source with any byte count.
566f48ad614SDennis Dalessandro  *
567f48ad614SDennis Dalessandro  * @pbuf: a number of blocks allocated within a PIO send context
568f48ad614SDennis Dalessandro  * @from: data source
569f48ad614SDennis Dalessandro  * @nbytes: number of bytes to copy
570f48ad614SDennis Dalessandro  */
seg_pio_copy_mid(struct pio_buf * pbuf,const void * from,size_t nbytes)571f48ad614SDennis Dalessandro void seg_pio_copy_mid(struct pio_buf *pbuf, const void *from, size_t nbytes)
572f48ad614SDennis Dalessandro {
573f48ad614SDennis Dalessandro 	unsigned long from_align = (unsigned long)from & 0x7;
574f48ad614SDennis Dalessandro 
575f48ad614SDennis Dalessandro 	if (pbuf->carry_bytes + nbytes < 8) {
576f48ad614SDennis Dalessandro 		/* not enough bytes to fill a QW */
577f48ad614SDennis Dalessandro 		read_extra_bytes(pbuf, from, nbytes);
578f48ad614SDennis Dalessandro 		return;
579f48ad614SDennis Dalessandro 	}
580f48ad614SDennis Dalessandro 
581f48ad614SDennis Dalessandro 	if (from_align) {
582f48ad614SDennis Dalessandro 		/* misaligned source pointer - align it */
583f48ad614SDennis Dalessandro 		unsigned long to_align;
584f48ad614SDennis Dalessandro 
585f48ad614SDennis Dalessandro 		/* bytes to read to align "from" */
586f48ad614SDennis Dalessandro 		to_align = 8 - from_align;
587f48ad614SDennis Dalessandro 
588f48ad614SDennis Dalessandro 		/*
589f48ad614SDennis Dalessandro 		 * In the advance-to-alignment logic below, we do not need
590f48ad614SDennis Dalessandro 		 * to check if we are using more than nbytes.  This is because
591f48ad614SDennis Dalessandro 		 * if we are here, we already know that carry+nbytes will
592f48ad614SDennis Dalessandro 		 * fill at least one QW.
593f48ad614SDennis Dalessandro 		 */
594f48ad614SDennis Dalessandro 		if (pbuf->carry_bytes + to_align < 8) {
595f48ad614SDennis Dalessandro 			/* not enough align bytes to fill a QW */
596f48ad614SDennis Dalessandro 			read_extra_bytes(pbuf, from, to_align);
597f48ad614SDennis Dalessandro 			from += to_align;
598f48ad614SDennis Dalessandro 			nbytes -= to_align;
599f48ad614SDennis Dalessandro 		} else {
600f48ad614SDennis Dalessandro 			/* bytes to fill carry */
601f48ad614SDennis Dalessandro 			unsigned long to_fill = 8 - pbuf->carry_bytes;
602f48ad614SDennis Dalessandro 			/* bytes left over to be read */
603f48ad614SDennis Dalessandro 			unsigned long extra = to_align - to_fill;
604f48ad614SDennis Dalessandro 			void __iomem *dest;
605f48ad614SDennis Dalessandro 
606f48ad614SDennis Dalessandro 			/* fill carry... */
607f48ad614SDennis Dalessandro 			read_extra_bytes(pbuf, from, to_fill);
608f48ad614SDennis Dalessandro 			from += to_fill;
609f48ad614SDennis Dalessandro 			nbytes -= to_fill;
6103e6c3b0fSSebastian Sanchez 			/* may not be enough valid bytes left to align */
6113e6c3b0fSSebastian Sanchez 			if (extra > nbytes)
6123e6c3b0fSSebastian Sanchez 				extra = nbytes;
613f48ad614SDennis Dalessandro 
614f48ad614SDennis Dalessandro 			/* ...now write carry */
615f48ad614SDennis Dalessandro 			dest = pbuf->start + (pbuf->qw_written * sizeof(u64));
616f48ad614SDennis Dalessandro 
617f48ad614SDennis Dalessandro 			/*
618f48ad614SDennis Dalessandro 			 * The two checks immediately below cannot both be
619f48ad614SDennis Dalessandro 			 * true, hence the else.  If we have wrapped, we
620f48ad614SDennis Dalessandro 			 * cannot still be within the first block.
621f48ad614SDennis Dalessandro 			 * Conversely, if we are still in the first block, we
622f48ad614SDennis Dalessandro 			 * cannot have wrapped.  We do the wrap check first
623f48ad614SDennis Dalessandro 			 * as that is more likely.
624f48ad614SDennis Dalessandro 			 */
625f48ad614SDennis Dalessandro 			/* adjust if we've wrapped */
626f48ad614SDennis Dalessandro 			if (dest >= pbuf->end)
6278af8d297SSebastian Sanchez 				dest -= pbuf->sc->size;
628f48ad614SDennis Dalessandro 			/* jump to SOP range if within the first block */
629f48ad614SDennis Dalessandro 			else if (pbuf->qw_written < PIO_BLOCK_QWS)
630f48ad614SDennis Dalessandro 				dest += SOP_DISTANCE;
631f48ad614SDennis Dalessandro 
632f48ad614SDennis Dalessandro 			carry8_write8(pbuf->carry, dest);
633f48ad614SDennis Dalessandro 			pbuf->qw_written++;
634f48ad614SDennis Dalessandro 
635f48ad614SDennis Dalessandro 			/* read any extra bytes to do final alignment */
636f48ad614SDennis Dalessandro 			/* this will overwrite anything in pbuf->carry */
637f48ad614SDennis Dalessandro 			read_low_bytes(pbuf, from, extra);
638f48ad614SDennis Dalessandro 			from += extra;
639f48ad614SDennis Dalessandro 			nbytes -= extra;
6403e6c3b0fSSebastian Sanchez 			/*
6413e6c3b0fSSebastian Sanchez 			 * If no bytes are left, return early - we are done.
6423e6c3b0fSSebastian Sanchez 			 * NOTE: This short-circuit is *required* because
6433e6c3b0fSSebastian Sanchez 			 * "extra" may have been reduced in size and "from"
6443e6c3b0fSSebastian Sanchez 			 * is not aligned, as required when leaving this
6453e6c3b0fSSebastian Sanchez 			 * if block.
6463e6c3b0fSSebastian Sanchez 			 */
6473e6c3b0fSSebastian Sanchez 			if (nbytes == 0)
6483e6c3b0fSSebastian Sanchez 				return;
649f48ad614SDennis Dalessandro 		}
650f48ad614SDennis Dalessandro 
651f48ad614SDennis Dalessandro 		/* at this point, from is QW aligned */
652f48ad614SDennis Dalessandro 	}
653f48ad614SDennis Dalessandro 
654f48ad614SDennis Dalessandro 	if (pbuf->carry_bytes)
655f48ad614SDennis Dalessandro 		mid_copy_mix(pbuf, from, nbytes);
656f48ad614SDennis Dalessandro 	else
657f48ad614SDennis Dalessandro 		mid_copy_straight(pbuf, from, nbytes);
658f48ad614SDennis Dalessandro }
659f48ad614SDennis Dalessandro 
660f48ad614SDennis Dalessandro /*
661f48ad614SDennis Dalessandro  * Segmented PIO Copy - end
662f48ad614SDennis Dalessandro  *
663f48ad614SDennis Dalessandro  * Write any remainder (in pbuf->carry) and finish writing the whole block.
664f48ad614SDennis Dalessandro  *
665f48ad614SDennis Dalessandro  * @pbuf: a number of blocks allocated within a PIO send context
666f48ad614SDennis Dalessandro  */
seg_pio_copy_end(struct pio_buf * pbuf)667f48ad614SDennis Dalessandro void seg_pio_copy_end(struct pio_buf *pbuf)
668f48ad614SDennis Dalessandro {
669f48ad614SDennis Dalessandro 	void __iomem *dest = pbuf->start + (pbuf->qw_written * sizeof(u64));
670f48ad614SDennis Dalessandro 
671f48ad614SDennis Dalessandro 	/*
672f48ad614SDennis Dalessandro 	 * The two checks immediately below cannot both be true, hence the
673f48ad614SDennis Dalessandro 	 * else.  If we have wrapped, we cannot still be within the first
674f48ad614SDennis Dalessandro 	 * block.  Conversely, if we are still in the first block, we
675f48ad614SDennis Dalessandro 	 * cannot have wrapped.  We do the wrap check first as that is
676f48ad614SDennis Dalessandro 	 * more likely.
677f48ad614SDennis Dalessandro 	 */
678f48ad614SDennis Dalessandro 	/* adjust if we have wrapped */
679f48ad614SDennis Dalessandro 	if (dest >= pbuf->end)
6808af8d297SSebastian Sanchez 		dest -= pbuf->sc->size;
681f48ad614SDennis Dalessandro 	/* jump to the SOP range if within the first block */
682f48ad614SDennis Dalessandro 	else if (pbuf->qw_written < PIO_BLOCK_QWS)
683f48ad614SDennis Dalessandro 		dest += SOP_DISTANCE;
684f48ad614SDennis Dalessandro 
685f48ad614SDennis Dalessandro 	/* write final bytes, if any */
686f48ad614SDennis Dalessandro 	if (carry_write8(pbuf, dest)) {
687f48ad614SDennis Dalessandro 		dest += sizeof(u64);
688f48ad614SDennis Dalessandro 		/*
689f48ad614SDennis Dalessandro 		 * NOTE: We do not need to recalculate whether dest needs
690f48ad614SDennis Dalessandro 		 * SOP_DISTANCE or not.
691f48ad614SDennis Dalessandro 		 *
692f48ad614SDennis Dalessandro 		 * If we are in the first block and the dangle write
693f48ad614SDennis Dalessandro 		 * keeps us in the same block, dest will need
694f48ad614SDennis Dalessandro 		 * to retain SOP_DISTANCE in the loop below.
695f48ad614SDennis Dalessandro 		 *
696f48ad614SDennis Dalessandro 		 * If we are in the first block and the dangle write pushes
697f48ad614SDennis Dalessandro 		 * us to the next block, then loop below will not run
698f48ad614SDennis Dalessandro 		 * and dest is not used.  Hence we do not need to update
699f48ad614SDennis Dalessandro 		 * it.
700f48ad614SDennis Dalessandro 		 *
701f48ad614SDennis Dalessandro 		 * If we are past the first block, then SOP_DISTANCE
702f48ad614SDennis Dalessandro 		 * was never added, so there is nothing to do.
703f48ad614SDennis Dalessandro 		 */
704f48ad614SDennis Dalessandro 	}
705f48ad614SDennis Dalessandro 
706f48ad614SDennis Dalessandro 	/* fill in rest of block */
707f48ad614SDennis Dalessandro 	while (((unsigned long)dest & PIO_BLOCK_MASK) != 0) {
708f48ad614SDennis Dalessandro 		writeq(0, dest);
709f48ad614SDennis Dalessandro 		dest += sizeof(u64);
710f48ad614SDennis Dalessandro 	}
711f48ad614SDennis Dalessandro 
712f48ad614SDennis Dalessandro 	/* finished with this buffer */
713f48ad614SDennis Dalessandro 	this_cpu_dec(*pbuf->sc->buffers_allocated);
714f48ad614SDennis Dalessandro 	preempt_enable();
715f48ad614SDennis Dalessandro }
716