Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: xiic: Fix big-endian register access

The driver tried to access device registers with the (little-endian)
iowrite/ioread functions. While this worked on little-endian machines
(e.g. Microblaze with AXI bus), it made the driver unusable on
big-endian machines (e.g. PPC405 with PLB).

During the probe function, the driver tried to write a 32-bit reset mask
into the reset register. This caused an error interrupt on big-endian
systems, because the device detected an invalid (byte-swapped) reset
mask. The result was an Oops.

The patch implements an endianness detection similar to the one used in
other Xilinx drivers like drivers/spi/spi-xilinx.c. It was tested on a
PPC405/PLB system.

Signed-off-by: Thomas Gessler <Thomas.Gessler@exp2.physik.uni-giessen.de>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>

authored by

Thomas Gessler and committed by
Wolfram Sang
48ef3ca9 a839ce66

+53 -5
+53 -5
drivers/i2c/busses/i2c-xiic.c
··· 46 46 STATE_START 47 47 }; 48 48 49 + enum xiic_endian { 50 + LITTLE, 51 + BIG 52 + }; 53 + 49 54 /** 50 55 * struct xiic_i2c - Internal representation of the XIIC I2C bus 51 56 * @base: Memory base of the HW registers ··· 75 70 enum xilinx_i2c_state state; 76 71 struct i2c_msg *rx_msg; 77 72 int rx_pos; 73 + enum xiic_endian endianness; 78 74 }; 79 75 80 76 ··· 176 170 static void xiic_start_xfer(struct xiic_i2c *i2c); 177 171 static void __xiic_start_xfer(struct xiic_i2c *i2c); 178 172 173 + /* 174 + * For the register read and write functions, a little-endian and big-endian 175 + * version are necessary. Endianness is detected during the probe function. 176 + * Only the least significant byte [doublet] of the register are ever 177 + * accessed. This requires an offset of 3 [2] from the base address for 178 + * big-endian systems. 179 + */ 180 + 179 181 static inline void xiic_setreg8(struct xiic_i2c *i2c, int reg, u8 value) 180 182 { 181 - iowrite8(value, i2c->base + reg); 183 + if (i2c->endianness == LITTLE) 184 + iowrite8(value, i2c->base + reg); 185 + else 186 + iowrite8(value, i2c->base + reg + 3); 182 187 } 183 188 184 189 static inline u8 xiic_getreg8(struct xiic_i2c *i2c, int reg) 185 190 { 186 - return ioread8(i2c->base + reg); 191 + u8 ret; 192 + 193 + if (i2c->endianness == LITTLE) 194 + ret = ioread8(i2c->base + reg); 195 + else 196 + ret = ioread8(i2c->base + reg + 3); 197 + return ret; 187 198 } 188 199 189 200 static inline void xiic_setreg16(struct xiic_i2c *i2c, int reg, u16 value) 190 201 { 191 - iowrite16(value, i2c->base + reg); 202 + if (i2c->endianness == LITTLE) 203 + iowrite16(value, i2c->base + reg); 204 + else 205 + iowrite16be(value, i2c->base + reg + 2); 192 206 } 193 207 194 208 static inline void xiic_setreg32(struct xiic_i2c *i2c, int reg, int value) 195 209 { 196 - iowrite32(value, i2c->base + reg); 210 + if (i2c->endianness == LITTLE) 211 + iowrite32(value, i2c->base + reg); 212 + else 213 + iowrite32be(value, i2c->base + reg); 197 214 } 198 215 199 216 static inline int xiic_getreg32(struct xiic_i2c *i2c, int reg) 200 217 { 201 - return ioread32(i2c->base + reg); 218 + u32 ret; 219 + 220 + if (i2c->endianness == LITTLE) 221 + ret = ioread32(i2c->base + reg); 222 + else 223 + ret = ioread32be(i2c->base + reg); 224 + return ret; 202 225 } 203 226 204 227 static inline void xiic_irq_dis(struct xiic_i2c *i2c, u32 mask) ··· 727 692 struct resource *res; 728 693 int ret, irq; 729 694 u8 i; 695 + u32 sr; 730 696 731 697 i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); 732 698 if (!i2c) ··· 759 723 dev_err(&pdev->dev, "Cannot claim IRQ\n"); 760 724 return ret; 761 725 } 726 + 727 + /* 728 + * Detect endianness 729 + * Try to reset the TX FIFO. Then check the EMPTY flag. If it is not 730 + * set, assume that the endianness was wrong and swap. 731 + */ 732 + i2c->endianness = LITTLE; 733 + xiic_setreg32(i2c, XIIC_CR_REG_OFFSET, XIIC_CR_TX_FIFO_RESET_MASK); 734 + /* Reset is cleared in xiic_reinit */ 735 + sr = xiic_getreg32(i2c, XIIC_SR_REG_OFFSET); 736 + if (!(sr & XIIC_SR_TX_FIFO_EMPTY_MASK)) 737 + i2c->endianness = BIG; 762 738 763 739 xiic_reinit(i2c); 764 740