1*5f5bac82SMichał Mirosław /* 2*5f5bac82SMichał Mirosław * cb710/sgbuf2.c 3*5f5bac82SMichał Mirosław * 4*5f5bac82SMichał Mirosław * Copyright by Michał Mirosław, 2008-2009 5*5f5bac82SMichał Mirosław * 6*5f5bac82SMichał Mirosław * This program is free software; you can redistribute it and/or modify 7*5f5bac82SMichał Mirosław * it under the terms of the GNU General Public License version 2 as 8*5f5bac82SMichał Mirosław * published by the Free Software Foundation. 9*5f5bac82SMichał Mirosław */ 10*5f5bac82SMichał Mirosław #include <linux/kernel.h> 11*5f5bac82SMichał Mirosław #include <linux/module.h> 12*5f5bac82SMichał Mirosław #include <linux/cb710.h> 13*5f5bac82SMichał Mirosław 14*5f5bac82SMichał Mirosław static bool sg_dwiter_next(struct sg_mapping_iter *miter) 15*5f5bac82SMichał Mirosław { 16*5f5bac82SMichał Mirosław if (sg_miter_next(miter)) { 17*5f5bac82SMichał Mirosław miter->consumed = 0; 18*5f5bac82SMichał Mirosław return true; 19*5f5bac82SMichał Mirosław } else 20*5f5bac82SMichał Mirosław return false; 21*5f5bac82SMichał Mirosław } 22*5f5bac82SMichał Mirosław 23*5f5bac82SMichał Mirosław static bool sg_dwiter_is_at_end(struct sg_mapping_iter *miter) 24*5f5bac82SMichał Mirosław { 25*5f5bac82SMichał Mirosław return miter->length == miter->consumed && !sg_dwiter_next(miter); 26*5f5bac82SMichał Mirosław } 27*5f5bac82SMichał Mirosław 28*5f5bac82SMichał Mirosław static uint32_t sg_dwiter_read_buffer(struct sg_mapping_iter *miter) 29*5f5bac82SMichał Mirosław { 30*5f5bac82SMichał Mirosław size_t len, left = 4; 31*5f5bac82SMichał Mirosław uint32_t data; 32*5f5bac82SMichał Mirosław void *addr = &data; 33*5f5bac82SMichał Mirosław 34*5f5bac82SMichał Mirosław do { 35*5f5bac82SMichał Mirosław len = min(miter->length - miter->consumed, left); 36*5f5bac82SMichał Mirosław memcpy(addr, miter->addr + miter->consumed, len); 37*5f5bac82SMichał Mirosław miter->consumed += len; 38*5f5bac82SMichał Mirosław left -= len; 39*5f5bac82SMichał Mirosław if (!left) 40*5f5bac82SMichał Mirosław return data; 41*5f5bac82SMichał Mirosław addr += len; 42*5f5bac82SMichał Mirosław } while (sg_dwiter_next(miter)); 43*5f5bac82SMichał Mirosław 44*5f5bac82SMichał Mirosław memset(addr, 0, left); 45*5f5bac82SMichał Mirosław return data; 46*5f5bac82SMichał Mirosław } 47*5f5bac82SMichał Mirosław 48*5f5bac82SMichał Mirosław static inline bool needs_unaligned_copy(const void *ptr) 49*5f5bac82SMichał Mirosław { 50*5f5bac82SMichał Mirosław #ifdef HAVE_EFFICIENT_UNALIGNED_ACCESS 51*5f5bac82SMichał Mirosław return false; 52*5f5bac82SMichał Mirosław #else 53*5f5bac82SMichał Mirosław return ((ptr - NULL) & 3) != 0; 54*5f5bac82SMichał Mirosław #endif 55*5f5bac82SMichał Mirosław } 56*5f5bac82SMichał Mirosław 57*5f5bac82SMichał Mirosław static bool sg_dwiter_get_next_block(struct sg_mapping_iter *miter, uint32_t **ptr) 58*5f5bac82SMichał Mirosław { 59*5f5bac82SMichał Mirosław size_t len; 60*5f5bac82SMichał Mirosław 61*5f5bac82SMichał Mirosław if (sg_dwiter_is_at_end(miter)) 62*5f5bac82SMichał Mirosław return true; 63*5f5bac82SMichał Mirosław 64*5f5bac82SMichał Mirosław len = miter->length - miter->consumed; 65*5f5bac82SMichał Mirosław 66*5f5bac82SMichał Mirosław if (likely(len >= 4 && !needs_unaligned_copy( 67*5f5bac82SMichał Mirosław miter->addr + miter->consumed))) { 68*5f5bac82SMichał Mirosław *ptr = miter->addr + miter->consumed; 69*5f5bac82SMichał Mirosław miter->consumed += 4; 70*5f5bac82SMichał Mirosław return true; 71*5f5bac82SMichał Mirosław } 72*5f5bac82SMichał Mirosław 73*5f5bac82SMichał Mirosław return false; 74*5f5bac82SMichał Mirosław } 75*5f5bac82SMichał Mirosław 76*5f5bac82SMichał Mirosław /** 77*5f5bac82SMichał Mirosław * cb710_sg_dwiter_read_next_block() - get next 32-bit word from sg buffer 78*5f5bac82SMichał Mirosław * @miter: sg mapping iterator used for reading 79*5f5bac82SMichał Mirosław * 80*5f5bac82SMichał Mirosław * Description: 81*5f5bac82SMichał Mirosław * Returns 32-bit word starting at byte pointed to by @miter@ 82*5f5bac82SMichał Mirosław * handling any alignment issues. Bytes past the buffer's end 83*5f5bac82SMichał Mirosław * are not accessed (read) but are returned as zeroes. @miter@ 84*5f5bac82SMichał Mirosław * is advanced by 4 bytes or to the end of buffer whichever is 85*5f5bac82SMichał Mirosław * closer. 86*5f5bac82SMichał Mirosław * 87*5f5bac82SMichał Mirosław * Context: 88*5f5bac82SMichał Mirosław * Same requirements as in sg_miter_next(). 89*5f5bac82SMichał Mirosław * 90*5f5bac82SMichał Mirosław * Returns: 91*5f5bac82SMichał Mirosław * 32-bit word just read. 92*5f5bac82SMichał Mirosław */ 93*5f5bac82SMichał Mirosław uint32_t cb710_sg_dwiter_read_next_block(struct sg_mapping_iter *miter) 94*5f5bac82SMichał Mirosław { 95*5f5bac82SMichał Mirosław uint32_t *ptr = NULL; 96*5f5bac82SMichał Mirosław 97*5f5bac82SMichał Mirosław if (likely(sg_dwiter_get_next_block(miter, &ptr))) 98*5f5bac82SMichał Mirosław return ptr ? *ptr : 0; 99*5f5bac82SMichał Mirosław 100*5f5bac82SMichał Mirosław return sg_dwiter_read_buffer(miter); 101*5f5bac82SMichał Mirosław } 102*5f5bac82SMichał Mirosław EXPORT_SYMBOL_GPL(cb710_sg_dwiter_read_next_block); 103*5f5bac82SMichał Mirosław 104*5f5bac82SMichał Mirosław static void sg_dwiter_write_slow(struct sg_mapping_iter *miter, uint32_t data) 105*5f5bac82SMichał Mirosław { 106*5f5bac82SMichał Mirosław size_t len, left = 4; 107*5f5bac82SMichał Mirosław void *addr = &data; 108*5f5bac82SMichał Mirosław 109*5f5bac82SMichał Mirosław do { 110*5f5bac82SMichał Mirosław len = min(miter->length - miter->consumed, left); 111*5f5bac82SMichał Mirosław memcpy(miter->addr, addr, len); 112*5f5bac82SMichał Mirosław miter->consumed += len; 113*5f5bac82SMichał Mirosław left -= len; 114*5f5bac82SMichał Mirosław if (!left) 115*5f5bac82SMichał Mirosław return; 116*5f5bac82SMichał Mirosław addr += len; 117*5f5bac82SMichał Mirosław flush_kernel_dcache_page(miter->page); 118*5f5bac82SMichał Mirosław } while (sg_dwiter_next(miter)); 119*5f5bac82SMichał Mirosław } 120*5f5bac82SMichał Mirosław 121*5f5bac82SMichał Mirosław /** 122*5f5bac82SMichał Mirosław * cb710_sg_dwiter_write_next_block() - write next 32-bit word to sg buffer 123*5f5bac82SMichał Mirosław * @miter: sg mapping iterator used for writing 124*5f5bac82SMichał Mirosław * 125*5f5bac82SMichał Mirosław * Description: 126*5f5bac82SMichał Mirosław * Writes 32-bit word starting at byte pointed to by @miter@ 127*5f5bac82SMichał Mirosław * handling any alignment issues. Bytes which would be written 128*5f5bac82SMichał Mirosław * past the buffer's end are silently discarded. @miter@ is 129*5f5bac82SMichał Mirosław * advanced by 4 bytes or to the end of buffer whichever is closer. 130*5f5bac82SMichał Mirosław * 131*5f5bac82SMichał Mirosław * Context: 132*5f5bac82SMichał Mirosław * Same requirements as in sg_miter_next(). 133*5f5bac82SMichał Mirosław */ 134*5f5bac82SMichał Mirosław void cb710_sg_dwiter_write_next_block(struct sg_mapping_iter *miter, uint32_t data) 135*5f5bac82SMichał Mirosław { 136*5f5bac82SMichał Mirosław uint32_t *ptr = NULL; 137*5f5bac82SMichał Mirosław 138*5f5bac82SMichał Mirosław if (likely(sg_dwiter_get_next_block(miter, &ptr))) { 139*5f5bac82SMichał Mirosław if (ptr) 140*5f5bac82SMichał Mirosław *ptr = data; 141*5f5bac82SMichał Mirosław else 142*5f5bac82SMichał Mirosław return; 143*5f5bac82SMichał Mirosław } else 144*5f5bac82SMichał Mirosław sg_dwiter_write_slow(miter, data); 145*5f5bac82SMichał Mirosław 146*5f5bac82SMichał Mirosław if (miter->length == miter->consumed) 147*5f5bac82SMichał Mirosław flush_kernel_dcache_page(miter->page); 148*5f5bac82SMichał Mirosław } 149*5f5bac82SMichał Mirosław EXPORT_SYMBOL_GPL(cb710_sg_dwiter_write_next_block); 150*5f5bac82SMichał Mirosław 151