nvmem: core: fix bit offsets of more than one byte
[ Upstream commit 7a06ef75107799675ea6e4d73b9df37e18e352a8 ] If the NVMEM specifies a stride to access data, reading particular cell might require bit offset that is bigger than one byte. Rework NVMEM core code to support bit offsets of more than 8 bits. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Link: https://lore.kernel.org/r/20250411112251.68002-9-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
d6abe0f6ad
commit
a4f865ecdb
+17
-7
@@ -824,7 +824,9 @@ static int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_nod
|
|||||||
if (addr && len == (2 * sizeof(u32))) {
|
if (addr && len == (2 * sizeof(u32))) {
|
||||||
info.bit_offset = be32_to_cpup(addr++);
|
info.bit_offset = be32_to_cpup(addr++);
|
||||||
info.nbits = be32_to_cpup(addr);
|
info.nbits = be32_to_cpup(addr);
|
||||||
if (info.bit_offset >= BITS_PER_BYTE || info.nbits < 1) {
|
if (info.bit_offset >= BITS_PER_BYTE * info.bytes ||
|
||||||
|
info.nbits < 1 ||
|
||||||
|
info.bit_offset + info.nbits > BITS_PER_BYTE * info.bytes) {
|
||||||
dev_err(dev, "nvmem: invalid bits on %pOF\n", child);
|
dev_err(dev, "nvmem: invalid bits on %pOF\n", child);
|
||||||
of_node_put(child);
|
of_node_put(child);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -1617,21 +1619,29 @@ EXPORT_SYMBOL_GPL(nvmem_cell_put);
|
|||||||
static void nvmem_shift_read_buffer_in_place(struct nvmem_cell_entry *cell, void *buf)
|
static void nvmem_shift_read_buffer_in_place(struct nvmem_cell_entry *cell, void *buf)
|
||||||
{
|
{
|
||||||
u8 *p, *b;
|
u8 *p, *b;
|
||||||
int i, extra, bit_offset = cell->bit_offset;
|
int i, extra, bytes_offset;
|
||||||
|
int bit_offset = cell->bit_offset;
|
||||||
|
|
||||||
p = b = buf;
|
p = b = buf;
|
||||||
if (bit_offset) {
|
|
||||||
|
bytes_offset = bit_offset / BITS_PER_BYTE;
|
||||||
|
b += bytes_offset;
|
||||||
|
bit_offset %= BITS_PER_BYTE;
|
||||||
|
|
||||||
|
if (bit_offset % BITS_PER_BYTE) {
|
||||||
/* First shift */
|
/* First shift */
|
||||||
*b++ >>= bit_offset;
|
*p = *b++ >> bit_offset;
|
||||||
|
|
||||||
/* setup rest of the bytes if any */
|
/* setup rest of the bytes if any */
|
||||||
for (i = 1; i < cell->bytes; i++) {
|
for (i = 1; i < cell->bytes; i++) {
|
||||||
/* Get bits from next byte and shift them towards msb */
|
/* Get bits from next byte and shift them towards msb */
|
||||||
*p |= *b << (BITS_PER_BYTE - bit_offset);
|
*p++ |= *b << (BITS_PER_BYTE - bit_offset);
|
||||||
|
|
||||||
p = b;
|
*p = *b++ >> bit_offset;
|
||||||
*b++ >>= bit_offset;
|
|
||||||
}
|
}
|
||||||
|
} else if (p != b) {
|
||||||
|
memmove(p, b, cell->bytes - bytes_offset);
|
||||||
|
p += cell->bytes - 1;
|
||||||
} else {
|
} else {
|
||||||
/* point to the msb */
|
/* point to the msb */
|
||||||
p += cell->bytes - 1;
|
p += cell->bytes - 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user