1 /* 2 * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> 3 * Copyright (C) 2008-2009 PetaLogix 4 * Copyright (C) 2007 John Williams 5 * 6 * Reasonably optimised generic C-code for memcpy on Microblaze 7 * This is generic C code to do efficient, alignment-aware memcpy. 8 * 9 * It is based on demo code originally Copyright 2001 by Intel Corp, taken from 10 * http://www.embedded.com/showArticle.jhtml?articleID=19205567 11 * 12 * Attempts were made, unsuccessfully, to contact the original 13 * author of this code (Michael Morrow, Intel). Below is the original 14 * copyright notice. 15 * 16 * This software has been developed by Intel Corporation. 17 * Intel specifically disclaims all warranties, express or 18 * implied, and all liability, including consequential and 19 * other indirect damages, for the use of this program, including 20 * liability for infringement of any proprietary rights, 21 * and including the warranties of merchantability and fitness 22 * for a particular purpose. Intel does not assume any 23 * responsibility for and errors which may appear in this program 24 * not any responsibility to update it. 25 */ 26 27 #include <linux/types.h> 28 #include <linux/stddef.h> 29 #include <linux/compiler.h> 30 #include <linux/module.h> 31 32 #include <linux/string.h> 33 #include <asm/system.h> 34 35 #ifdef __HAVE_ARCH_MEMCPY 36 #ifndef CONFIG_OPT_LIB_FUNCTION 37 void *memcpy(void *v_dst, const void *v_src, __kernel_size_t c) 38 { 39 const char *src = v_src; 40 char *dst = v_dst; 41 42 /* Simple, byte oriented memcpy. */ 43 while (c--) 44 *dst++ = *src++; 45 46 return v_dst; 47 } 48 #else /* CONFIG_OPT_LIB_FUNCTION */ 49 void *memcpy(void *v_dst, const void *v_src, __kernel_size_t c) 50 { 51 const char *src = v_src; 52 char *dst = v_dst; 53 54 /* The following code tries to optimize the copy by using unsigned 55 * alignment. This will work fine if both source and destination are 56 * aligned on the same boundary. However, if they are aligned on 57 * different boundaries shifts will be necessary. This might result in 58 * bad performance on MicroBlaze systems without a barrel shifter. 59 */ 60 const uint32_t *i_src; 61 uint32_t *i_dst; 62 63 if (likely(c >= 4)) { 64 unsigned value, buf_hold; 65 66 /* Align the dstination to a word boundry. */ 67 /* This is done in an endian independant manner. */ 68 switch ((unsigned long)dst & 3) { 69 case 1: 70 *dst++ = *src++; 71 --c; 72 case 2: 73 *dst++ = *src++; 74 --c; 75 case 3: 76 *dst++ = *src++; 77 --c; 78 } 79 80 i_dst = (void *)dst; 81 82 /* Choose a copy scheme based on the source */ 83 /* alignment relative to dstination. */ 84 switch ((unsigned long)src & 3) { 85 case 0x0: /* Both byte offsets are aligned */ 86 i_src = (const void *)src; 87 88 for (; c >= 4; c -= 4) 89 *i_dst++ = *i_src++; 90 91 src = (const void *)i_src; 92 break; 93 case 0x1: /* Unaligned - Off by 1 */ 94 /* Word align the source */ 95 i_src = (const void *) ((unsigned)src & ~3); 96 #ifndef __MICROBLAZEEL__ 97 /* Load the holding buffer */ 98 buf_hold = *i_src++ << 8; 99 100 for (; c >= 4; c -= 4) { 101 value = *i_src++; 102 *i_dst++ = buf_hold | value >> 24; 103 buf_hold = value << 8; 104 } 105 #else 106 /* Load the holding buffer */ 107 buf_hold = (*i_src++ & 0xFFFFFF00) >>8; 108 109 for (; c >= 4; c -= 4) { 110 value = *i_src++; 111 *i_dst++ = buf_hold | ((value & 0xFF) << 24); 112 buf_hold = (value & 0xFFFFFF00) >>8; 113 } 114 #endif 115 /* Realign the source */ 116 src = (const void *)i_src; 117 src -= 3; 118 break; 119 case 0x2: /* Unaligned - Off by 2 */ 120 /* Word align the source */ 121 i_src = (const void *) ((unsigned)src & ~3); 122 #ifndef __MICROBLAZEEL__ 123 /* Load the holding buffer */ 124 buf_hold = *i_src++ << 16; 125 126 for (; c >= 4; c -= 4) { 127 value = *i_src++; 128 *i_dst++ = buf_hold | value >> 16; 129 buf_hold = value << 16; 130 } 131 #else 132 /* Load the holding buffer */ 133 buf_hold = (*i_src++ & 0xFFFF0000 )>>16; 134 135 for (; c >= 4; c -= 4) { 136 value = *i_src++; 137 *i_dst++ = buf_hold | ((value & 0xFFFF)<<16); 138 buf_hold = (value & 0xFFFF0000) >>16; 139 } 140 #endif 141 /* Realign the source */ 142 src = (const void *)i_src; 143 src -= 2; 144 break; 145 case 0x3: /* Unaligned - Off by 3 */ 146 /* Word align the source */ 147 i_src = (const void *) ((unsigned)src & ~3); 148 #ifndef __MICROBLAZEEL__ 149 /* Load the holding buffer */ 150 buf_hold = *i_src++ << 24; 151 152 for (; c >= 4; c -= 4) { 153 value = *i_src++; 154 *i_dst++ = buf_hold | value >> 8; 155 buf_hold = value << 24; 156 } 157 #else 158 /* Load the holding buffer */ 159 buf_hold = (*i_src++ & 0xFF000000) >> 24; 160 161 for (; c >= 4; c -= 4) { 162 value = *i_src++; 163 *i_dst++ = buf_hold | ((value & 0xFFFFFF) << 8); 164 buf_hold = (value & 0xFF000000) >> 24; 165 } 166 #endif 167 /* Realign the source */ 168 src = (const void *)i_src; 169 src -= 1; 170 break; 171 } 172 dst = (void *)i_dst; 173 } 174 175 /* Finish off any remaining bytes */ 176 /* simple fast copy, ... unless a cache boundry is crossed */ 177 switch (c) { 178 case 3: 179 *dst++ = *src++; 180 case 2: 181 *dst++ = *src++; 182 case 1: 183 *dst++ = *src++; 184 } 185 186 return v_dst; 187 } 188 #endif /* CONFIG_OPT_LIB_FUNCTION */ 189 EXPORT_SYMBOL(memcpy); 190 #endif /* __HAVE_ARCH_MEMCPY */ 191