xref: /linux/drivers/misc/cb710/sgbuf2.c (revision 5f5bac8272be791b67c7b7b411e7c8c5847e598a)
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