18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright(c) 2015, 2016 Intel Corporation. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 58c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 118c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 148c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 168c2ecf20Sopenharmony_ci * General Public License for more details. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * BSD LICENSE 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 218c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 228c2ecf20Sopenharmony_ci * are met: 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above copyright 258c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 268c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above copyright 278c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 288c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 298c2ecf20Sopenharmony_ci * distribution. 308c2ecf20Sopenharmony_ci * - Neither the name of Intel Corporation nor the names of its 318c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 328c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 358c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 368c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 378c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 388c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 398c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 408c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 418c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 428c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 438c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 448c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include "hfi.h" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* additive distance between non-SOP and SOP space */ 518c2ecf20Sopenharmony_ci#define SOP_DISTANCE (TXE_PIO_SIZE / 2) 528c2ecf20Sopenharmony_ci#define PIO_BLOCK_MASK (PIO_BLOCK_SIZE - 1) 538c2ecf20Sopenharmony_ci/* number of QUADWORDs in a block */ 548c2ecf20Sopenharmony_ci#define PIO_BLOCK_QWS (PIO_BLOCK_SIZE / sizeof(u64)) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/** 578c2ecf20Sopenharmony_ci * pio_copy - copy data block to MMIO space 588c2ecf20Sopenharmony_ci * @pbuf: a number of blocks allocated within a PIO send context 598c2ecf20Sopenharmony_ci * @pbc: PBC to send 608c2ecf20Sopenharmony_ci * @from: source, must be 8 byte aligned 618c2ecf20Sopenharmony_ci * @count: number of DWORD (32-bit) quantities to copy from source 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Copy data from source to PIO Send Buffer memory, 8 bytes at a time. 648c2ecf20Sopenharmony_ci * Must always write full BLOCK_SIZE bytes blocks. The first block must 658c2ecf20Sopenharmony_ci * be written to the corresponding SOP=1 address. 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * Known: 688c2ecf20Sopenharmony_ci * o pbuf->start always starts on a block boundary 698c2ecf20Sopenharmony_ci * o pbuf can wrap only at a block boundary 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_civoid pio_copy(struct hfi1_devdata *dd, struct pio_buf *pbuf, u64 pbc, 728c2ecf20Sopenharmony_ci const void *from, size_t count) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci void __iomem *dest = pbuf->start + SOP_DISTANCE; 758c2ecf20Sopenharmony_ci void __iomem *send = dest + PIO_BLOCK_SIZE; 768c2ecf20Sopenharmony_ci void __iomem *dend; /* 8-byte data end */ 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* write the PBC */ 798c2ecf20Sopenharmony_ci writeq(pbc, dest); 808c2ecf20Sopenharmony_ci dest += sizeof(u64); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* calculate where the QWORD data ends - in SOP=1 space */ 838c2ecf20Sopenharmony_ci dend = dest + ((count >> 1) * sizeof(u64)); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (dend < send) { 868c2ecf20Sopenharmony_ci /* 878c2ecf20Sopenharmony_ci * all QWORD data is within the SOP block, does *not* 888c2ecf20Sopenharmony_ci * reach the end of the SOP block 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci while (dest < dend) { 928c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 938c2ecf20Sopenharmony_ci from += sizeof(u64); 948c2ecf20Sopenharmony_ci dest += sizeof(u64); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci /* 978c2ecf20Sopenharmony_ci * No boundary checks are needed here: 988c2ecf20Sopenharmony_ci * 0. We're not on the SOP block boundary 998c2ecf20Sopenharmony_ci * 1. The possible DWORD dangle will still be within 1008c2ecf20Sopenharmony_ci * the SOP block 1018c2ecf20Sopenharmony_ci * 2. We cannot wrap except on a block boundary. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci } else { 1048c2ecf20Sopenharmony_ci /* QWORD data extends _to_ or beyond the SOP block */ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* write 8-byte SOP chunk data */ 1078c2ecf20Sopenharmony_ci while (dest < send) { 1088c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 1098c2ecf20Sopenharmony_ci from += sizeof(u64); 1108c2ecf20Sopenharmony_ci dest += sizeof(u64); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci /* drop out of the SOP range */ 1138c2ecf20Sopenharmony_ci dest -= SOP_DISTANCE; 1148c2ecf20Sopenharmony_ci dend -= SOP_DISTANCE; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * If the wrap comes before or matches the data end, 1188c2ecf20Sopenharmony_ci * copy until until the wrap, then wrap. 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * If the data ends at the end of the SOP above and 1218c2ecf20Sopenharmony_ci * the buffer wraps, then pbuf->end == dend == dest 1228c2ecf20Sopenharmony_ci * and nothing will get written, but we will wrap in 1238c2ecf20Sopenharmony_ci * case there is a dangling DWORD. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_ci if (pbuf->end <= dend) { 1268c2ecf20Sopenharmony_ci while (dest < pbuf->end) { 1278c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 1288c2ecf20Sopenharmony_ci from += sizeof(u64); 1298c2ecf20Sopenharmony_ci dest += sizeof(u64); 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci dest -= pbuf->sc->size; 1338c2ecf20Sopenharmony_ci dend -= pbuf->sc->size; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* write 8-byte non-SOP, non-wrap chunk data */ 1378c2ecf20Sopenharmony_ci while (dest < dend) { 1388c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 1398c2ecf20Sopenharmony_ci from += sizeof(u64); 1408c2ecf20Sopenharmony_ci dest += sizeof(u64); 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci /* at this point we have wrapped if we are going to wrap */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* write dangling u32, if any */ 1468c2ecf20Sopenharmony_ci if (count & 1) { 1478c2ecf20Sopenharmony_ci union mix val; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci val.val64 = 0; 1508c2ecf20Sopenharmony_ci val.val32[0] = *(u32 *)from; 1518c2ecf20Sopenharmony_ci writeq(val.val64, dest); 1528c2ecf20Sopenharmony_ci dest += sizeof(u64); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * fill in rest of block, no need to check pbuf->end 1568c2ecf20Sopenharmony_ci * as we only wrap on a block boundary 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci while (((unsigned long)dest & PIO_BLOCK_MASK) != 0) { 1598c2ecf20Sopenharmony_ci writeq(0, dest); 1608c2ecf20Sopenharmony_ci dest += sizeof(u64); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* finished with this buffer */ 1648c2ecf20Sopenharmony_ci this_cpu_dec(*pbuf->sc->buffers_allocated); 1658c2ecf20Sopenharmony_ci preempt_enable(); 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* 1698c2ecf20Sopenharmony_ci * Handle carry bytes using shifts and masks. 1708c2ecf20Sopenharmony_ci * 1718c2ecf20Sopenharmony_ci * NOTE: the value the unused portion of carry is expected to always be zero. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* 1758c2ecf20Sopenharmony_ci * "zero" shift - bit shift used to zero out upper bytes. Input is 1768c2ecf20Sopenharmony_ci * the count of LSB bytes to preserve. 1778c2ecf20Sopenharmony_ci */ 1788c2ecf20Sopenharmony_ci#define zshift(x) (8 * (8 - (x))) 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci/* 1818c2ecf20Sopenharmony_ci * "merge" shift - bit shift used to merge with carry bytes. Input is 1828c2ecf20Sopenharmony_ci * the LSB byte count to move beyond. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci#define mshift(x) (8 * (x)) 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * Jump copy - no-loop copy for < 8 bytes. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_cistatic inline void jcopy(u8 *dest, const u8 *src, u32 n) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci switch (n) { 1928c2ecf20Sopenharmony_ci case 7: 1938c2ecf20Sopenharmony_ci *dest++ = *src++; 1948c2ecf20Sopenharmony_ci fallthrough; 1958c2ecf20Sopenharmony_ci case 6: 1968c2ecf20Sopenharmony_ci *dest++ = *src++; 1978c2ecf20Sopenharmony_ci fallthrough; 1988c2ecf20Sopenharmony_ci case 5: 1998c2ecf20Sopenharmony_ci *dest++ = *src++; 2008c2ecf20Sopenharmony_ci fallthrough; 2018c2ecf20Sopenharmony_ci case 4: 2028c2ecf20Sopenharmony_ci *dest++ = *src++; 2038c2ecf20Sopenharmony_ci fallthrough; 2048c2ecf20Sopenharmony_ci case 3: 2058c2ecf20Sopenharmony_ci *dest++ = *src++; 2068c2ecf20Sopenharmony_ci fallthrough; 2078c2ecf20Sopenharmony_ci case 2: 2088c2ecf20Sopenharmony_ci *dest++ = *src++; 2098c2ecf20Sopenharmony_ci fallthrough; 2108c2ecf20Sopenharmony_ci case 1: 2118c2ecf20Sopenharmony_ci *dest++ = *src++; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci/* 2168c2ecf20Sopenharmony_ci * Read nbytes from "from" and and place them in the low bytes 2178c2ecf20Sopenharmony_ci * of pbuf->carry. Other bytes are left as-is. Any previous 2188c2ecf20Sopenharmony_ci * value in pbuf->carry is lost. 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci * NOTES: 2218c2ecf20Sopenharmony_ci * o do not read from from if nbytes is zero 2228c2ecf20Sopenharmony_ci * o from may _not_ be u64 aligned. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_cistatic inline void read_low_bytes(struct pio_buf *pbuf, const void *from, 2258c2ecf20Sopenharmony_ci unsigned int nbytes) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci pbuf->carry.val64 = 0; 2288c2ecf20Sopenharmony_ci jcopy(&pbuf->carry.val8[0], from, nbytes); 2298c2ecf20Sopenharmony_ci pbuf->carry_bytes = nbytes; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * Read nbytes bytes from "from" and put them at the end of pbuf->carry. 2348c2ecf20Sopenharmony_ci * It is expected that the extra read does not overfill carry. 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * NOTES: 2378c2ecf20Sopenharmony_ci * o from may _not_ be u64 aligned 2388c2ecf20Sopenharmony_ci * o nbytes may span a QW boundary 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistatic inline void read_extra_bytes(struct pio_buf *pbuf, 2418c2ecf20Sopenharmony_ci const void *from, unsigned int nbytes) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci jcopy(&pbuf->carry.val8[pbuf->carry_bytes], from, nbytes); 2448c2ecf20Sopenharmony_ci pbuf->carry_bytes += nbytes; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * Write a quad word using parts of pbuf->carry and the next 8 bytes of src. 2498c2ecf20Sopenharmony_ci * Put the unused part of the next 8 bytes of src into the LSB bytes of 2508c2ecf20Sopenharmony_ci * pbuf->carry with the upper bytes zeroed.. 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * NOTES: 2538c2ecf20Sopenharmony_ci * o result must keep unused bytes zeroed 2548c2ecf20Sopenharmony_ci * o src must be u64 aligned 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_cistatic inline void merge_write8( 2578c2ecf20Sopenharmony_ci struct pio_buf *pbuf, 2588c2ecf20Sopenharmony_ci void __iomem *dest, 2598c2ecf20Sopenharmony_ci const void *src) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci u64 new, temp; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci new = *(u64 *)src; 2648c2ecf20Sopenharmony_ci temp = pbuf->carry.val64 | (new << mshift(pbuf->carry_bytes)); 2658c2ecf20Sopenharmony_ci writeq(temp, dest); 2668c2ecf20Sopenharmony_ci pbuf->carry.val64 = new >> zshift(pbuf->carry_bytes); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* 2708c2ecf20Sopenharmony_ci * Write a quad word using all bytes of carry. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_cistatic inline void carry8_write8(union mix carry, void __iomem *dest) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci writeq(carry.val64, dest); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/* 2788c2ecf20Sopenharmony_ci * Write a quad word using all the valid bytes of carry. If carry 2798c2ecf20Sopenharmony_ci * has zero valid bytes, nothing is written. 2808c2ecf20Sopenharmony_ci * Returns 0 on nothing written, non-zero on quad word written. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_cistatic inline int carry_write8(struct pio_buf *pbuf, void __iomem *dest) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci if (pbuf->carry_bytes) { 2858c2ecf20Sopenharmony_ci /* unused bytes are always kept zeroed, so just write */ 2868c2ecf20Sopenharmony_ci writeq(pbuf->carry.val64, dest); 2878c2ecf20Sopenharmony_ci return 1; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci/* 2948c2ecf20Sopenharmony_ci * Segmented PIO Copy - start 2958c2ecf20Sopenharmony_ci * 2968c2ecf20Sopenharmony_ci * Start a PIO copy. 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * @pbuf: destination buffer 2998c2ecf20Sopenharmony_ci * @pbc: the PBC for the PIO buffer 3008c2ecf20Sopenharmony_ci * @from: data source, QWORD aligned 3018c2ecf20Sopenharmony_ci * @nbytes: bytes to copy 3028c2ecf20Sopenharmony_ci */ 3038c2ecf20Sopenharmony_civoid seg_pio_copy_start(struct pio_buf *pbuf, u64 pbc, 3048c2ecf20Sopenharmony_ci const void *from, size_t nbytes) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci void __iomem *dest = pbuf->start + SOP_DISTANCE; 3078c2ecf20Sopenharmony_ci void __iomem *send = dest + PIO_BLOCK_SIZE; 3088c2ecf20Sopenharmony_ci void __iomem *dend; /* 8-byte data end */ 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci writeq(pbc, dest); 3118c2ecf20Sopenharmony_ci dest += sizeof(u64); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* calculate where the QWORD data ends - in SOP=1 space */ 3148c2ecf20Sopenharmony_ci dend = dest + ((nbytes >> 3) * sizeof(u64)); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (dend < send) { 3178c2ecf20Sopenharmony_ci /* 3188c2ecf20Sopenharmony_ci * all QWORD data is within the SOP block, does *not* 3198c2ecf20Sopenharmony_ci * reach the end of the SOP block 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci while (dest < dend) { 3238c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 3248c2ecf20Sopenharmony_ci from += sizeof(u64); 3258c2ecf20Sopenharmony_ci dest += sizeof(u64); 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci /* 3288c2ecf20Sopenharmony_ci * No boundary checks are needed here: 3298c2ecf20Sopenharmony_ci * 0. We're not on the SOP block boundary 3308c2ecf20Sopenharmony_ci * 1. The possible DWORD dangle will still be within 3318c2ecf20Sopenharmony_ci * the SOP block 3328c2ecf20Sopenharmony_ci * 2. We cannot wrap except on a block boundary. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ci } else { 3358c2ecf20Sopenharmony_ci /* QWORD data extends _to_ or beyond the SOP block */ 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* write 8-byte SOP chunk data */ 3388c2ecf20Sopenharmony_ci while (dest < send) { 3398c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 3408c2ecf20Sopenharmony_ci from += sizeof(u64); 3418c2ecf20Sopenharmony_ci dest += sizeof(u64); 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci /* drop out of the SOP range */ 3448c2ecf20Sopenharmony_ci dest -= SOP_DISTANCE; 3458c2ecf20Sopenharmony_ci dend -= SOP_DISTANCE; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* 3488c2ecf20Sopenharmony_ci * If the wrap comes before or matches the data end, 3498c2ecf20Sopenharmony_ci * copy until until the wrap, then wrap. 3508c2ecf20Sopenharmony_ci * 3518c2ecf20Sopenharmony_ci * If the data ends at the end of the SOP above and 3528c2ecf20Sopenharmony_ci * the buffer wraps, then pbuf->end == dend == dest 3538c2ecf20Sopenharmony_ci * and nothing will get written, but we will wrap in 3548c2ecf20Sopenharmony_ci * case there is a dangling DWORD. 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci if (pbuf->end <= dend) { 3578c2ecf20Sopenharmony_ci while (dest < pbuf->end) { 3588c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 3598c2ecf20Sopenharmony_ci from += sizeof(u64); 3608c2ecf20Sopenharmony_ci dest += sizeof(u64); 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci dest -= pbuf->sc->size; 3648c2ecf20Sopenharmony_ci dend -= pbuf->sc->size; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* write 8-byte non-SOP, non-wrap chunk data */ 3688c2ecf20Sopenharmony_ci while (dest < dend) { 3698c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 3708c2ecf20Sopenharmony_ci from += sizeof(u64); 3718c2ecf20Sopenharmony_ci dest += sizeof(u64); 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci /* at this point we have wrapped if we are going to wrap */ 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* ...but it doesn't matter as we're done writing */ 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* save dangling bytes, if any */ 3798c2ecf20Sopenharmony_ci read_low_bytes(pbuf, from, nbytes & 0x7); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci pbuf->qw_written = 1 /*PBC*/ + (nbytes >> 3); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/* 3858c2ecf20Sopenharmony_ci * Mid copy helper, "mixed case" - source is 64-bit aligned but carry 3868c2ecf20Sopenharmony_ci * bytes are non-zero. 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * Whole u64s must be written to the chip, so bytes must be manually merged. 3898c2ecf20Sopenharmony_ci * 3908c2ecf20Sopenharmony_ci * @pbuf: destination buffer 3918c2ecf20Sopenharmony_ci * @from: data source, is QWORD aligned. 3928c2ecf20Sopenharmony_ci * @nbytes: bytes to copy 3938c2ecf20Sopenharmony_ci * 3948c2ecf20Sopenharmony_ci * Must handle nbytes < 8. 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_cistatic void mid_copy_mix(struct pio_buf *pbuf, const void *from, size_t nbytes) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci void __iomem *dest = pbuf->start + (pbuf->qw_written * sizeof(u64)); 3998c2ecf20Sopenharmony_ci void __iomem *dend; /* 8-byte data end */ 4008c2ecf20Sopenharmony_ci unsigned long qw_to_write = nbytes >> 3; 4018c2ecf20Sopenharmony_ci unsigned long bytes_left = nbytes & 0x7; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* calculate 8-byte data end */ 4048c2ecf20Sopenharmony_ci dend = dest + (qw_to_write * sizeof(u64)); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (pbuf->qw_written < PIO_BLOCK_QWS) { 4078c2ecf20Sopenharmony_ci /* 4088c2ecf20Sopenharmony_ci * Still within SOP block. We don't need to check for 4098c2ecf20Sopenharmony_ci * wrap because we are still in the first block and 4108c2ecf20Sopenharmony_ci * can only wrap on block boundaries. 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci void __iomem *send; /* SOP end */ 4138c2ecf20Sopenharmony_ci void __iomem *xend; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* 4168c2ecf20Sopenharmony_ci * calculate the end of data or end of block, whichever 4178c2ecf20Sopenharmony_ci * comes first 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci send = pbuf->start + PIO_BLOCK_SIZE; 4208c2ecf20Sopenharmony_ci xend = min(send, dend); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* shift up to SOP=1 space */ 4238c2ecf20Sopenharmony_ci dest += SOP_DISTANCE; 4248c2ecf20Sopenharmony_ci xend += SOP_DISTANCE; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci /* write 8-byte chunk data */ 4278c2ecf20Sopenharmony_ci while (dest < xend) { 4288c2ecf20Sopenharmony_ci merge_write8(pbuf, dest, from); 4298c2ecf20Sopenharmony_ci from += sizeof(u64); 4308c2ecf20Sopenharmony_ci dest += sizeof(u64); 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* shift down to SOP=0 space */ 4348c2ecf20Sopenharmony_ci dest -= SOP_DISTANCE; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci /* 4378c2ecf20Sopenharmony_ci * At this point dest could be (either, both, or neither): 4388c2ecf20Sopenharmony_ci * - at dend 4398c2ecf20Sopenharmony_ci * - at the wrap 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* 4438c2ecf20Sopenharmony_ci * If the wrap comes before or matches the data end, 4448c2ecf20Sopenharmony_ci * copy until until the wrap, then wrap. 4458c2ecf20Sopenharmony_ci * 4468c2ecf20Sopenharmony_ci * If dest is at the wrap, we will fall into the if, 4478c2ecf20Sopenharmony_ci * not do the loop, when wrap. 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * If the data ends at the end of the SOP above and 4508c2ecf20Sopenharmony_ci * the buffer wraps, then pbuf->end == dend == dest 4518c2ecf20Sopenharmony_ci * and nothing will get written. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci if (pbuf->end <= dend) { 4548c2ecf20Sopenharmony_ci while (dest < pbuf->end) { 4558c2ecf20Sopenharmony_ci merge_write8(pbuf, dest, from); 4568c2ecf20Sopenharmony_ci from += sizeof(u64); 4578c2ecf20Sopenharmony_ci dest += sizeof(u64); 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci dest -= pbuf->sc->size; 4618c2ecf20Sopenharmony_ci dend -= pbuf->sc->size; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci /* write 8-byte non-SOP, non-wrap chunk data */ 4658c2ecf20Sopenharmony_ci while (dest < dend) { 4668c2ecf20Sopenharmony_ci merge_write8(pbuf, dest, from); 4678c2ecf20Sopenharmony_ci from += sizeof(u64); 4688c2ecf20Sopenharmony_ci dest += sizeof(u64); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci pbuf->qw_written += qw_to_write; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci /* handle carry and left-over bytes */ 4748c2ecf20Sopenharmony_ci if (pbuf->carry_bytes + bytes_left >= 8) { 4758c2ecf20Sopenharmony_ci unsigned long nread; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* there is enough to fill another qw - fill carry */ 4788c2ecf20Sopenharmony_ci nread = 8 - pbuf->carry_bytes; 4798c2ecf20Sopenharmony_ci read_extra_bytes(pbuf, from, nread); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* 4828c2ecf20Sopenharmony_ci * One more write - but need to make sure dest is correct. 4838c2ecf20Sopenharmony_ci * Check for wrap and the possibility the write 4848c2ecf20Sopenharmony_ci * should be in SOP space. 4858c2ecf20Sopenharmony_ci * 4868c2ecf20Sopenharmony_ci * The two checks immediately below cannot both be true, hence 4878c2ecf20Sopenharmony_ci * the else. If we have wrapped, we cannot still be within the 4888c2ecf20Sopenharmony_ci * first block. Conversely, if we are still in the first block, 4898c2ecf20Sopenharmony_ci * we cannot have wrapped. We do the wrap check first as that 4908c2ecf20Sopenharmony_ci * is more likely. 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci /* adjust if we have wrapped */ 4938c2ecf20Sopenharmony_ci if (dest >= pbuf->end) 4948c2ecf20Sopenharmony_ci dest -= pbuf->sc->size; 4958c2ecf20Sopenharmony_ci /* jump to the SOP range if within the first block */ 4968c2ecf20Sopenharmony_ci else if (pbuf->qw_written < PIO_BLOCK_QWS) 4978c2ecf20Sopenharmony_ci dest += SOP_DISTANCE; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci /* flush out full carry */ 5008c2ecf20Sopenharmony_ci carry8_write8(pbuf->carry, dest); 5018c2ecf20Sopenharmony_ci pbuf->qw_written++; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* now adjust and read the rest of the bytes into carry */ 5048c2ecf20Sopenharmony_ci bytes_left -= nread; 5058c2ecf20Sopenharmony_ci from += nread; /* from is now not aligned */ 5068c2ecf20Sopenharmony_ci read_low_bytes(pbuf, from, bytes_left); 5078c2ecf20Sopenharmony_ci } else { 5088c2ecf20Sopenharmony_ci /* not enough to fill another qw, append the rest to carry */ 5098c2ecf20Sopenharmony_ci read_extra_bytes(pbuf, from, bytes_left); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci/* 5148c2ecf20Sopenharmony_ci * Mid copy helper, "straight case" - source pointer is 64-bit aligned 5158c2ecf20Sopenharmony_ci * with no carry bytes. 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * @pbuf: destination buffer 5188c2ecf20Sopenharmony_ci * @from: data source, is QWORD aligned 5198c2ecf20Sopenharmony_ci * @nbytes: bytes to copy 5208c2ecf20Sopenharmony_ci * 5218c2ecf20Sopenharmony_ci * Must handle nbytes < 8. 5228c2ecf20Sopenharmony_ci */ 5238c2ecf20Sopenharmony_cistatic void mid_copy_straight(struct pio_buf *pbuf, 5248c2ecf20Sopenharmony_ci const void *from, size_t nbytes) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci void __iomem *dest = pbuf->start + (pbuf->qw_written * sizeof(u64)); 5278c2ecf20Sopenharmony_ci void __iomem *dend; /* 8-byte data end */ 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* calculate 8-byte data end */ 5308c2ecf20Sopenharmony_ci dend = dest + ((nbytes >> 3) * sizeof(u64)); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (pbuf->qw_written < PIO_BLOCK_QWS) { 5338c2ecf20Sopenharmony_ci /* 5348c2ecf20Sopenharmony_ci * Still within SOP block. We don't need to check for 5358c2ecf20Sopenharmony_ci * wrap because we are still in the first block and 5368c2ecf20Sopenharmony_ci * can only wrap on block boundaries. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_ci void __iomem *send; /* SOP end */ 5398c2ecf20Sopenharmony_ci void __iomem *xend; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* 5428c2ecf20Sopenharmony_ci * calculate the end of data or end of block, whichever 5438c2ecf20Sopenharmony_ci * comes first 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_ci send = pbuf->start + PIO_BLOCK_SIZE; 5468c2ecf20Sopenharmony_ci xend = min(send, dend); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* shift up to SOP=1 space */ 5498c2ecf20Sopenharmony_ci dest += SOP_DISTANCE; 5508c2ecf20Sopenharmony_ci xend += SOP_DISTANCE; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* write 8-byte chunk data */ 5538c2ecf20Sopenharmony_ci while (dest < xend) { 5548c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 5558c2ecf20Sopenharmony_ci from += sizeof(u64); 5568c2ecf20Sopenharmony_ci dest += sizeof(u64); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* shift down to SOP=0 space */ 5608c2ecf20Sopenharmony_ci dest -= SOP_DISTANCE; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci /* 5638c2ecf20Sopenharmony_ci * At this point dest could be (either, both, or neither): 5648c2ecf20Sopenharmony_ci * - at dend 5658c2ecf20Sopenharmony_ci * - at the wrap 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* 5698c2ecf20Sopenharmony_ci * If the wrap comes before or matches the data end, 5708c2ecf20Sopenharmony_ci * copy until until the wrap, then wrap. 5718c2ecf20Sopenharmony_ci * 5728c2ecf20Sopenharmony_ci * If dest is at the wrap, we will fall into the if, 5738c2ecf20Sopenharmony_ci * not do the loop, when wrap. 5748c2ecf20Sopenharmony_ci * 5758c2ecf20Sopenharmony_ci * If the data ends at the end of the SOP above and 5768c2ecf20Sopenharmony_ci * the buffer wraps, then pbuf->end == dend == dest 5778c2ecf20Sopenharmony_ci * and nothing will get written. 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci if (pbuf->end <= dend) { 5808c2ecf20Sopenharmony_ci while (dest < pbuf->end) { 5818c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 5828c2ecf20Sopenharmony_ci from += sizeof(u64); 5838c2ecf20Sopenharmony_ci dest += sizeof(u64); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci dest -= pbuf->sc->size; 5878c2ecf20Sopenharmony_ci dend -= pbuf->sc->size; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* write 8-byte non-SOP, non-wrap chunk data */ 5918c2ecf20Sopenharmony_ci while (dest < dend) { 5928c2ecf20Sopenharmony_ci writeq(*(u64 *)from, dest); 5938c2ecf20Sopenharmony_ci from += sizeof(u64); 5948c2ecf20Sopenharmony_ci dest += sizeof(u64); 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* we know carry_bytes was zero on entry to this routine */ 5988c2ecf20Sopenharmony_ci read_low_bytes(pbuf, from, nbytes & 0x7); 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci pbuf->qw_written += nbytes >> 3; 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/* 6048c2ecf20Sopenharmony_ci * Segmented PIO Copy - middle 6058c2ecf20Sopenharmony_ci * 6068c2ecf20Sopenharmony_ci * Must handle any aligned tail and any aligned source with any byte count. 6078c2ecf20Sopenharmony_ci * 6088c2ecf20Sopenharmony_ci * @pbuf: a number of blocks allocated within a PIO send context 6098c2ecf20Sopenharmony_ci * @from: data source 6108c2ecf20Sopenharmony_ci * @nbytes: number of bytes to copy 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_civoid seg_pio_copy_mid(struct pio_buf *pbuf, const void *from, size_t nbytes) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci unsigned long from_align = (unsigned long)from & 0x7; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (pbuf->carry_bytes + nbytes < 8) { 6178c2ecf20Sopenharmony_ci /* not enough bytes to fill a QW */ 6188c2ecf20Sopenharmony_ci read_extra_bytes(pbuf, from, nbytes); 6198c2ecf20Sopenharmony_ci return; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (from_align) { 6238c2ecf20Sopenharmony_ci /* misaligned source pointer - align it */ 6248c2ecf20Sopenharmony_ci unsigned long to_align; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci /* bytes to read to align "from" */ 6278c2ecf20Sopenharmony_ci to_align = 8 - from_align; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci /* 6308c2ecf20Sopenharmony_ci * In the advance-to-alignment logic below, we do not need 6318c2ecf20Sopenharmony_ci * to check if we are using more than nbytes. This is because 6328c2ecf20Sopenharmony_ci * if we are here, we already know that carry+nbytes will 6338c2ecf20Sopenharmony_ci * fill at least one QW. 6348c2ecf20Sopenharmony_ci */ 6358c2ecf20Sopenharmony_ci if (pbuf->carry_bytes + to_align < 8) { 6368c2ecf20Sopenharmony_ci /* not enough align bytes to fill a QW */ 6378c2ecf20Sopenharmony_ci read_extra_bytes(pbuf, from, to_align); 6388c2ecf20Sopenharmony_ci from += to_align; 6398c2ecf20Sopenharmony_ci nbytes -= to_align; 6408c2ecf20Sopenharmony_ci } else { 6418c2ecf20Sopenharmony_ci /* bytes to fill carry */ 6428c2ecf20Sopenharmony_ci unsigned long to_fill = 8 - pbuf->carry_bytes; 6438c2ecf20Sopenharmony_ci /* bytes left over to be read */ 6448c2ecf20Sopenharmony_ci unsigned long extra = to_align - to_fill; 6458c2ecf20Sopenharmony_ci void __iomem *dest; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* fill carry... */ 6488c2ecf20Sopenharmony_ci read_extra_bytes(pbuf, from, to_fill); 6498c2ecf20Sopenharmony_ci from += to_fill; 6508c2ecf20Sopenharmony_ci nbytes -= to_fill; 6518c2ecf20Sopenharmony_ci /* may not be enough valid bytes left to align */ 6528c2ecf20Sopenharmony_ci if (extra > nbytes) 6538c2ecf20Sopenharmony_ci extra = nbytes; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* ...now write carry */ 6568c2ecf20Sopenharmony_ci dest = pbuf->start + (pbuf->qw_written * sizeof(u64)); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci /* 6598c2ecf20Sopenharmony_ci * The two checks immediately below cannot both be 6608c2ecf20Sopenharmony_ci * true, hence the else. If we have wrapped, we 6618c2ecf20Sopenharmony_ci * cannot still be within the first block. 6628c2ecf20Sopenharmony_ci * Conversely, if we are still in the first block, we 6638c2ecf20Sopenharmony_ci * cannot have wrapped. We do the wrap check first 6648c2ecf20Sopenharmony_ci * as that is more likely. 6658c2ecf20Sopenharmony_ci */ 6668c2ecf20Sopenharmony_ci /* adjust if we've wrapped */ 6678c2ecf20Sopenharmony_ci if (dest >= pbuf->end) 6688c2ecf20Sopenharmony_ci dest -= pbuf->sc->size; 6698c2ecf20Sopenharmony_ci /* jump to SOP range if within the first block */ 6708c2ecf20Sopenharmony_ci else if (pbuf->qw_written < PIO_BLOCK_QWS) 6718c2ecf20Sopenharmony_ci dest += SOP_DISTANCE; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci carry8_write8(pbuf->carry, dest); 6748c2ecf20Sopenharmony_ci pbuf->qw_written++; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci /* read any extra bytes to do final alignment */ 6778c2ecf20Sopenharmony_ci /* this will overwrite anything in pbuf->carry */ 6788c2ecf20Sopenharmony_ci read_low_bytes(pbuf, from, extra); 6798c2ecf20Sopenharmony_ci from += extra; 6808c2ecf20Sopenharmony_ci nbytes -= extra; 6818c2ecf20Sopenharmony_ci /* 6828c2ecf20Sopenharmony_ci * If no bytes are left, return early - we are done. 6838c2ecf20Sopenharmony_ci * NOTE: This short-circuit is *required* because 6848c2ecf20Sopenharmony_ci * "extra" may have been reduced in size and "from" 6858c2ecf20Sopenharmony_ci * is not aligned, as required when leaving this 6868c2ecf20Sopenharmony_ci * if block. 6878c2ecf20Sopenharmony_ci */ 6888c2ecf20Sopenharmony_ci if (nbytes == 0) 6898c2ecf20Sopenharmony_ci return; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* at this point, from is QW aligned */ 6938c2ecf20Sopenharmony_ci } 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (pbuf->carry_bytes) 6968c2ecf20Sopenharmony_ci mid_copy_mix(pbuf, from, nbytes); 6978c2ecf20Sopenharmony_ci else 6988c2ecf20Sopenharmony_ci mid_copy_straight(pbuf, from, nbytes); 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci/* 7028c2ecf20Sopenharmony_ci * Segmented PIO Copy - end 7038c2ecf20Sopenharmony_ci * 7048c2ecf20Sopenharmony_ci * Write any remainder (in pbuf->carry) and finish writing the whole block. 7058c2ecf20Sopenharmony_ci * 7068c2ecf20Sopenharmony_ci * @pbuf: a number of blocks allocated within a PIO send context 7078c2ecf20Sopenharmony_ci */ 7088c2ecf20Sopenharmony_civoid seg_pio_copy_end(struct pio_buf *pbuf) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci void __iomem *dest = pbuf->start + (pbuf->qw_written * sizeof(u64)); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci /* 7138c2ecf20Sopenharmony_ci * The two checks immediately below cannot both be true, hence the 7148c2ecf20Sopenharmony_ci * else. If we have wrapped, we cannot still be within the first 7158c2ecf20Sopenharmony_ci * block. Conversely, if we are still in the first block, we 7168c2ecf20Sopenharmony_ci * cannot have wrapped. We do the wrap check first as that is 7178c2ecf20Sopenharmony_ci * more likely. 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_ci /* adjust if we have wrapped */ 7208c2ecf20Sopenharmony_ci if (dest >= pbuf->end) 7218c2ecf20Sopenharmony_ci dest -= pbuf->sc->size; 7228c2ecf20Sopenharmony_ci /* jump to the SOP range if within the first block */ 7238c2ecf20Sopenharmony_ci else if (pbuf->qw_written < PIO_BLOCK_QWS) 7248c2ecf20Sopenharmony_ci dest += SOP_DISTANCE; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* write final bytes, if any */ 7278c2ecf20Sopenharmony_ci if (carry_write8(pbuf, dest)) { 7288c2ecf20Sopenharmony_ci dest += sizeof(u64); 7298c2ecf20Sopenharmony_ci /* 7308c2ecf20Sopenharmony_ci * NOTE: We do not need to recalculate whether dest needs 7318c2ecf20Sopenharmony_ci * SOP_DISTANCE or not. 7328c2ecf20Sopenharmony_ci * 7338c2ecf20Sopenharmony_ci * If we are in the first block and the dangle write 7348c2ecf20Sopenharmony_ci * keeps us in the same block, dest will need 7358c2ecf20Sopenharmony_ci * to retain SOP_DISTANCE in the loop below. 7368c2ecf20Sopenharmony_ci * 7378c2ecf20Sopenharmony_ci * If we are in the first block and the dangle write pushes 7388c2ecf20Sopenharmony_ci * us to the next block, then loop below will not run 7398c2ecf20Sopenharmony_ci * and dest is not used. Hence we do not need to update 7408c2ecf20Sopenharmony_ci * it. 7418c2ecf20Sopenharmony_ci * 7428c2ecf20Sopenharmony_ci * If we are past the first block, then SOP_DISTANCE 7438c2ecf20Sopenharmony_ci * was never added, so there is nothing to do. 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* fill in rest of block */ 7488c2ecf20Sopenharmony_ci while (((unsigned long)dest & PIO_BLOCK_MASK) != 0) { 7498c2ecf20Sopenharmony_ci writeq(0, dest); 7508c2ecf20Sopenharmony_ci dest += sizeof(u64); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* finished with this buffer */ 7548c2ecf20Sopenharmony_ci this_cpu_dec(*pbuf->sc->buffers_allocated); 7558c2ecf20Sopenharmony_ci preempt_enable(); 7568c2ecf20Sopenharmony_ci} 757