162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  cb710/sgbuf2.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright by Michał Mirosław, 2008-2009
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/cb710.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic bool sg_dwiter_next(struct sg_mapping_iter *miter)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	if (sg_miter_next(miter)) {
1462306a36Sopenharmony_ci		miter->consumed = 0;
1562306a36Sopenharmony_ci		return true;
1662306a36Sopenharmony_ci	} else
1762306a36Sopenharmony_ci		return false;
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic bool sg_dwiter_is_at_end(struct sg_mapping_iter *miter)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	return miter->length == miter->consumed && !sg_dwiter_next(miter);
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic uint32_t sg_dwiter_read_buffer(struct sg_mapping_iter *miter)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	size_t len, left = 4;
2862306a36Sopenharmony_ci	uint32_t data;
2962306a36Sopenharmony_ci	void *addr = &data;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	do {
3262306a36Sopenharmony_ci		len = min(miter->length - miter->consumed, left);
3362306a36Sopenharmony_ci		memcpy(addr, miter->addr + miter->consumed, len);
3462306a36Sopenharmony_ci		miter->consumed += len;
3562306a36Sopenharmony_ci		left -= len;
3662306a36Sopenharmony_ci		if (!left)
3762306a36Sopenharmony_ci			return data;
3862306a36Sopenharmony_ci		addr += len;
3962306a36Sopenharmony_ci	} while (sg_dwiter_next(miter));
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	memset(addr, 0, left);
4262306a36Sopenharmony_ci	return data;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic inline bool needs_unaligned_copy(const void *ptr)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
4862306a36Sopenharmony_ci	return false;
4962306a36Sopenharmony_ci#else
5062306a36Sopenharmony_ci	return ((uintptr_t)ptr & 3) != 0;
5162306a36Sopenharmony_ci#endif
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic bool sg_dwiter_get_next_block(struct sg_mapping_iter *miter, uint32_t **ptr)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	size_t len;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	if (sg_dwiter_is_at_end(miter))
5962306a36Sopenharmony_ci		return true;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	len = miter->length - miter->consumed;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (likely(len >= 4 && !needs_unaligned_copy(
6462306a36Sopenharmony_ci			miter->addr + miter->consumed))) {
6562306a36Sopenharmony_ci		*ptr = miter->addr + miter->consumed;
6662306a36Sopenharmony_ci		miter->consumed += 4;
6762306a36Sopenharmony_ci		return true;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return false;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/**
7462306a36Sopenharmony_ci * cb710_sg_dwiter_read_next_block() - get next 32-bit word from sg buffer
7562306a36Sopenharmony_ci * @miter: sg mapping iterator used for reading
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * Description:
7862306a36Sopenharmony_ci *   Returns 32-bit word starting at byte pointed to by @miter@
7962306a36Sopenharmony_ci *   handling any alignment issues.  Bytes past the buffer's end
8062306a36Sopenharmony_ci *   are not accessed (read) but are returned as zeroes.  @miter@
8162306a36Sopenharmony_ci *   is advanced by 4 bytes or to the end of buffer whichever is
8262306a36Sopenharmony_ci *   closer.
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci * Context:
8562306a36Sopenharmony_ci *   Same requirements as in sg_miter_next().
8662306a36Sopenharmony_ci *
8762306a36Sopenharmony_ci * Returns:
8862306a36Sopenharmony_ci *   32-bit word just read.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_ciuint32_t cb710_sg_dwiter_read_next_block(struct sg_mapping_iter *miter)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	uint32_t *ptr = NULL;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (likely(sg_dwiter_get_next_block(miter, &ptr)))
9562306a36Sopenharmony_ci		return ptr ? *ptr : 0;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return sg_dwiter_read_buffer(miter);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cb710_sg_dwiter_read_next_block);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void sg_dwiter_write_slow(struct sg_mapping_iter *miter, uint32_t data)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	size_t len, left = 4;
10462306a36Sopenharmony_ci	void *addr = &data;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	do {
10762306a36Sopenharmony_ci		len = min(miter->length - miter->consumed, left);
10862306a36Sopenharmony_ci		memcpy(miter->addr, addr, len);
10962306a36Sopenharmony_ci		miter->consumed += len;
11062306a36Sopenharmony_ci		left -= len;
11162306a36Sopenharmony_ci		if (!left)
11262306a36Sopenharmony_ci			return;
11362306a36Sopenharmony_ci		addr += len;
11462306a36Sopenharmony_ci	} while (sg_dwiter_next(miter));
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/**
11862306a36Sopenharmony_ci * cb710_sg_dwiter_write_next_block() - write next 32-bit word to sg buffer
11962306a36Sopenharmony_ci * @miter: sg mapping iterator used for writing
12062306a36Sopenharmony_ci * @data: data to write to sg buffer
12162306a36Sopenharmony_ci *
12262306a36Sopenharmony_ci * Description:
12362306a36Sopenharmony_ci *   Writes 32-bit word starting at byte pointed to by @miter@
12462306a36Sopenharmony_ci *   handling any alignment issues.  Bytes which would be written
12562306a36Sopenharmony_ci *   past the buffer's end are silently discarded. @miter@ is
12662306a36Sopenharmony_ci *   advanced by 4 bytes or to the end of buffer whichever is closer.
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * Context:
12962306a36Sopenharmony_ci *   Same requirements as in sg_miter_next().
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_civoid cb710_sg_dwiter_write_next_block(struct sg_mapping_iter *miter, uint32_t data)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	uint32_t *ptr = NULL;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (likely(sg_dwiter_get_next_block(miter, &ptr))) {
13662306a36Sopenharmony_ci		if (ptr)
13762306a36Sopenharmony_ci			*ptr = data;
13862306a36Sopenharmony_ci		else
13962306a36Sopenharmony_ci			return;
14062306a36Sopenharmony_ci	} else
14162306a36Sopenharmony_ci		sg_dwiter_write_slow(miter, data);
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cb710_sg_dwiter_write_next_block);
14462306a36Sopenharmony_ci
145