mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Short MMIO transfers that are not a multiple of four bytes in size need
a special case for the final bytes, however the existing implementation
is not endian-safe and introduces an incorrect byteswap on big-endian
kernels.
This usually does not cause problems because most systems are
little-endian and most transfers are multiple of four bytes long, but
still needs to be fixed to avoid the extra byteswap.
Change the special case for both i3c_writel_fifo() and i3c_readl_fifo()
to use non-byteswapping writesl() and readsl() with a single element
instead of the byteswapping writel()/readl() that are meant for individual
MMIO registers. As data is copied between a FIFO and a memory buffer,
the writesl()/readsl() loops are typically based on __raw_readl()/
__raw_writel(), resulting in the order of bytes in the FIFO to match
the order in the buffer, regardless of the CPU endianess.
The earlier versions in the dw-i3c and i3c-master-cdns had a correct
implementation, but the generic version that was recently added broke it.
Fixes: 733b439375 ("i3c: master: Add inline i3c_readl_fifo() and i3c_writel_fifo()")
Cc: Manikanta Guntupalli <manikanta.guntupalli@amd.com>
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Reviewed-by: Jorge Marques <jorge.marques@analog.com>
Link: https://lore.kernel.org/r/20250924201837.3691486-1-arnd@kernel.org
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
		
	
			
		
			
				
	
	
		
			71 lines
		
	
	
	
		
			1.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			71 lines
		
	
	
	
		
			1.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* SPDX-License-Identifier: GPL-2.0 */
 | 
						|
/*
 | 
						|
 * Copyright (C) 2018 Cadence Design Systems Inc.
 | 
						|
 *
 | 
						|
 * Author: Boris Brezillon <boris.brezillon@bootlin.com>
 | 
						|
 */
 | 
						|
 | 
						|
#ifndef I3C_INTERNALS_H
 | 
						|
#define I3C_INTERNALS_H
 | 
						|
 | 
						|
#include <linux/i3c/master.h>
 | 
						|
#include <linux/io.h>
 | 
						|
 | 
						|
void i3c_bus_normaluse_lock(struct i3c_bus *bus);
 | 
						|
void i3c_bus_normaluse_unlock(struct i3c_bus *bus);
 | 
						|
 | 
						|
int i3c_dev_setdasa_locked(struct i3c_dev_desc *dev);
 | 
						|
int i3c_dev_do_priv_xfers_locked(struct i3c_dev_desc *dev,
 | 
						|
				 struct i3c_priv_xfer *xfers,
 | 
						|
				 int nxfers);
 | 
						|
int i3c_dev_disable_ibi_locked(struct i3c_dev_desc *dev);
 | 
						|
int i3c_dev_enable_ibi_locked(struct i3c_dev_desc *dev);
 | 
						|
int i3c_dev_request_ibi_locked(struct i3c_dev_desc *dev,
 | 
						|
			       const struct i3c_ibi_setup *req);
 | 
						|
void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev);
 | 
						|
 | 
						|
/**
 | 
						|
 * i3c_writel_fifo - Write data buffer to 32bit FIFO
 | 
						|
 * @addr: FIFO Address to write to
 | 
						|
 * @buf: Pointer to the data bytes to write
 | 
						|
 * @nbytes: Number of bytes to write
 | 
						|
 */
 | 
						|
static inline void i3c_writel_fifo(void __iomem *addr, const void *buf,
 | 
						|
				   int nbytes)
 | 
						|
{
 | 
						|
	writesl(addr, buf, nbytes / 4);
 | 
						|
	if (nbytes & 3) {
 | 
						|
		u32 tmp = 0;
 | 
						|
 | 
						|
		memcpy(&tmp, buf + (nbytes & ~3), nbytes & 3);
 | 
						|
		/*
 | 
						|
		 * writesl() instead of writel() to keep FIFO
 | 
						|
		 * byteorder on big-endian targets
 | 
						|
		 */
 | 
						|
		writesl(addr, &tmp, 1);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * i3c_readl_fifo - Read data buffer from 32bit FIFO
 | 
						|
 * @addr: FIFO Address to read from
 | 
						|
 * @buf: Pointer to the buffer to store read bytes
 | 
						|
 * @nbytes: Number of bytes to read
 | 
						|
 */
 | 
						|
static inline void i3c_readl_fifo(const void __iomem *addr, void *buf,
 | 
						|
				  int nbytes)
 | 
						|
{
 | 
						|
	readsl(addr, buf, nbytes / 4);
 | 
						|
	if (nbytes & 3) {
 | 
						|
		u32 tmp;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * readsl() instead of readl() to keep FIFO
 | 
						|
		 * byteorder on big-endian targets
 | 
						|
		 */
 | 
						|
		readsl(addr, &tmp, 1);
 | 
						|
		memcpy(buf + (nbytes & ~3), &tmp, nbytes & 3);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
#endif /* I3C_INTERNAL_H */
 |