[BUG?] crypto: caam: little/big endianness on ARM vs PPC

Steffen Trumtrar s.trumtrar at pengutronix.de
Tue Jun 16 00:45:34 PDT 2015


Hi!

On Mon, Jun 15, 2015 at 05:28:16PM +0100, Russell King - ARM Linux wrote:
> On Mon, Jun 15, 2015 at 05:59:07PM +0200, Steffen Trumtrar wrote:
> > I'm working on CAAM support for the ARM-based i.MX6 SoCs. The current
> > drivers/crypto/caam driver only works for PowerPC AFAIK.
> > Actually, there isn't that much to do, to get support for the i.MX6 but
> > one patch breaks the driver severely:
> > 
> > 	commit ef94b1d834aace7101de77c3a7c2631b9ae9c5f6
> > 	crypto: caam - Add definition of rd/wr_reg64 for little endian platform
> 
> You're not the only one who's hitting problems with this - Jon Nettleton
> has been working on it recently.
> 
> The way this has been done is fairly yucky to start with: several things
> about it are particularly horrid.  The first is the repetitive code
> for the BE and LE cases, when all that's actually different is the
> register order between the two code cases.
> 
> The second thing is the excessive use of masking - I'm pretty sure the
> compiler won't complain with the below.
> 
> diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h
> index 378ddc17f60e..ba0fa630a25d 100644
> --- a/drivers/crypto/caam/regs.h
> +++ b/drivers/crypto/caam/regs.h
> @@ -84,34 +84,28 @@
>  #endif
>  
>  #ifndef CONFIG_64BIT
> -#ifdef __BIG_ENDIAN
> -static inline void wr_reg64(u64 __iomem *reg, u64 data)
> -{
> -	wr_reg32((u32 __iomem *)reg, (data & 0xffffffff00000000ull) >> 32);
> -	wr_reg32((u32 __iomem *)reg + 1, data & 0x00000000ffffffffull);
> -}
> -
> -static inline u64 rd_reg64(u64 __iomem *reg)
> -{
> -	return (((u64)rd_reg32((u32 __iomem *)reg)) << 32) |
> -		((u64)rd_reg32((u32 __iomem *)reg + 1));
> -}
> +#if defined(__BIG_ENDIAN)
> +#define REG64_HI32(reg) ((u32 __iomem *)(reg))
> +#define REG64_LO32(reg) ((u32 __iomem *)(reg) + 1)
> +#elif defined(__LITTLE_ENDIAN)
> +#define REG64_HI32(reg) ((u32 __iomem *)(reg) + 1)
> +#define REG64_LO32(reg) ((u32 __iomem *)(reg))
>  #else
> -#ifdef __LITTLE_ENDIAN
> +#error Broken endian?
> +#endif
> +
>  static inline void wr_reg64(u64 __iomem *reg, u64 data)
>  {
> -	wr_reg32((u32 __iomem *)reg + 1, (data & 0xffffffff00000000ull) >> 32);
> -	wr_reg32((u32 __iomem *)reg, data & 0x00000000ffffffffull);
> +	wr_reg32(REG64_HI32(reg), data >> 32);
> +	wr_reg32(REG64_LO32(reg), data);
>  }
>  
>  static inline u64 rd_reg64(u64 __iomem *reg)
>  {
> -	return (((u64)rd_reg32((u32 __iomem *)reg + 1)) << 32) |
> -		((u64)rd_reg32((u32 __iomem *)reg));
> +	return ((u64)rd_reg32(REG64_HI32(reg))) << 32 |
> +		rd_reg32(REG64_LO32(reg));
>  }
>  #endif
> -#endif
> -#endif
>  
>  /*
>   * jr_outentry
> 
> The second issue is that apparently, the register order doesn't actually
> change for LE devices - in other words, the byte order within each register
> does change, but they aren't a 64-bit register, they're two separate 32-bit
> registers.  So, they should always be written as such.
> 
> So, I'd get rid of the #if defined(__BIG_ENDIAN) stuff and instead have:
> 
> +/*
> + * The DMA address register is a pair of 32-bit registers, and should
> + * always be accessed as such.
> + */
> +#define REG64_HI32(reg) ((u32 __iomem *)(reg))
> +#define REG64_LO32(reg) ((u32 __iomem *)(reg) + 1)
> 

Thanks for all your explanations (in the other mail, too).
I, personally, like this approach the best; at least in the current state
of the driver.

Thanks,
Steffen

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |



More information about the linux-arm-kernel mailing list