]> err.no Git - linux-2.6/commitdiff
[POWERPC] Add bootwrapper support for Marvell/mv64x60 I2C
authorMark A. Greer <mgreer@mvista.com>
Sat, 12 May 2007 00:54:53 +0000 (10:54 +1000)
committerPaul Mackerras <paulus@samba.org>
Sat, 12 May 2007 01:32:49 +0000 (11:32 +1000)
Some platforms support a variety processor modules with no method of
determining which exact processor module is being used except by
examining Vital Product Data (VPD).  The modules may have different
amounts of memory, clock frequencies, etc. so reading the VPD becomes
necessary to correctly set properties in the device tree before its
passed to the kernel.

Often the VPD is stored in I2C EEPROMs so an I2C driver becomes necessary.
This I2C driver is for the I2C controller that's embedded on the Marvel
mv64x60 line of host bridges.

Signed-off-by: Mark A. Greer <mgreer@mvista.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
arch/powerpc/boot/Makefile
arch/powerpc/boot/mv64x60_i2c.c [new file with mode: 0644]

index 71b284b2f1794b4a81b354c5f8fe5ed349d7b940..12fd57e11fa56dc0976f758deb3b9bd55c369155 100644 (file)
@@ -43,7 +43,7 @@ $(addprefix $(obj)/,$(zlib) gunzip_util.o main.o): \
 src-wlib := string.S crt0.S stdio.c main.c flatdevtree.c flatdevtree_misc.c \
                ns16550.c serial.c simple_alloc.c div64.S util.S \
                gunzip_util.c elf_util.c $(zlib) devtree.c \
-               44x.c ebony.c mv64x60.c mpsc.c
+               44x.c ebony.c mv64x60.c mpsc.c mv64x60_i2c.c
 src-plat := of.c cuboot-83xx.c cuboot-85xx.c holly.c \
                cuboot-ebony.c treeboot-ebony.c
 src-boot := $(src-wlib) $(src-plat) empty.c
diff --git a/arch/powerpc/boot/mv64x60_i2c.c b/arch/powerpc/boot/mv64x60_i2c.c
new file mode 100644 (file)
index 0000000..435fe85
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Bootloader version of the i2c driver for the MV64x60.
+ *
+ * Author: Dale Farnsworth <dfarnsworth@mvista.com>
+ * Maintained by: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2003, 2007 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program is
+ * licensed "as is" without any warranty of any kind, whether express or
+ * implied.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include "types.h"
+#include "elf.h"
+#include "page.h"
+#include "string.h"
+#include "stdio.h"
+#include "io.h"
+#include "ops.h"
+#include "mv64x60.h"
+
+extern void udelay(long);
+
+/* Register defines */
+#define MV64x60_I2C_REG_SLAVE_ADDR                     0x00
+#define MV64x60_I2C_REG_DATA                           0x04
+#define MV64x60_I2C_REG_CONTROL                                0x08
+#define MV64x60_I2C_REG_STATUS                         0x0c
+#define MV64x60_I2C_REG_BAUD                           0x0c
+#define MV64x60_I2C_REG_EXT_SLAVE_ADDR                 0x10
+#define MV64x60_I2C_REG_SOFT_RESET                     0x1c
+
+#define MV64x60_I2C_CONTROL_ACK                                0x04
+#define MV64x60_I2C_CONTROL_IFLG                       0x08
+#define MV64x60_I2C_CONTROL_STOP                       0x10
+#define MV64x60_I2C_CONTROL_START                      0x20
+#define MV64x60_I2C_CONTROL_TWSIEN                     0x40
+#define MV64x60_I2C_CONTROL_INTEN                      0x80
+
+#define MV64x60_I2C_STATUS_BUS_ERR                     0x00
+#define MV64x60_I2C_STATUS_MAST_START                  0x08
+#define MV64x60_I2C_STATUS_MAST_REPEAT_START           0x10
+#define MV64x60_I2C_STATUS_MAST_WR_ADDR_ACK            0x18
+#define MV64x60_I2C_STATUS_MAST_WR_ADDR_NO_ACK         0x20
+#define MV64x60_I2C_STATUS_MAST_WR_ACK                 0x28
+#define MV64x60_I2C_STATUS_MAST_WR_NO_ACK              0x30
+#define MV64x60_I2C_STATUS_MAST_LOST_ARB               0x38
+#define MV64x60_I2C_STATUS_MAST_RD_ADDR_ACK            0x40
+#define MV64x60_I2C_STATUS_MAST_RD_ADDR_NO_ACK         0x48
+#define MV64x60_I2C_STATUS_MAST_RD_DATA_ACK            0x50
+#define MV64x60_I2C_STATUS_MAST_RD_DATA_NO_ACK         0x58
+#define MV64x60_I2C_STATUS_MAST_WR_ADDR_2_ACK          0xd0
+#define MV64x60_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK       0xd8
+#define MV64x60_I2C_STATUS_MAST_RD_ADDR_2_ACK          0xe0
+#define MV64x60_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK       0xe8
+#define MV64x60_I2C_STATUS_NO_STATUS                   0xf8
+
+static u8 *ctlr_base;
+
+static int mv64x60_i2c_wait_for_status(int wanted)
+{
+       int i;
+       int status;
+
+       for (i=0; i<1000; i++) {
+               udelay(10);
+               status = in_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_STATUS))
+                       & 0xff;
+               if (status == wanted)
+                       return status;
+       }
+       return -status;
+}
+
+static int mv64x60_i2c_control(int control, int status)
+{
+       out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_CONTROL), control & 0xff);
+       return mv64x60_i2c_wait_for_status(status);
+}
+
+static int mv64x60_i2c_read_byte(int control, int status)
+{
+       out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_CONTROL), control & 0xff);
+       if (mv64x60_i2c_wait_for_status(status) < 0)
+               return -1;
+       return in_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_DATA)) & 0xff;
+}
+
+static int mv64x60_i2c_write_byte(int data, int control, int status)
+{
+       out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_DATA), data & 0xff);
+       out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_CONTROL), control & 0xff);
+       return mv64x60_i2c_wait_for_status(status);
+}
+
+int mv64x60_i2c_read(u32 devaddr, u8 *buf, u32 offset, u32 offset_size,
+                u32 count)
+{
+       int i;
+       int data;
+       int control;
+       int status;
+
+       if (ctlr_base == NULL)
+               return -1;
+
+       /* send reset */
+       out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_SOFT_RESET), 0);
+       out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_SLAVE_ADDR), 0);
+       out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_EXT_SLAVE_ADDR), 0);
+       out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_BAUD), (4 << 3) | 0x4);
+
+       if (mv64x60_i2c_control(MV64x60_I2C_CONTROL_TWSIEN,
+                               MV64x60_I2C_STATUS_NO_STATUS) < 0)
+               return -1;
+
+       /* send start */
+       control = MV64x60_I2C_CONTROL_START | MV64x60_I2C_CONTROL_TWSIEN;
+       status = MV64x60_I2C_STATUS_MAST_START;
+       if (mv64x60_i2c_control(control, status) < 0)
+               return -1;
+
+       /* select device for writing */
+       data = devaddr & ~0x1;
+       control = MV64x60_I2C_CONTROL_TWSIEN;
+       status = MV64x60_I2C_STATUS_MAST_WR_ADDR_ACK;
+       if (mv64x60_i2c_write_byte(data, control, status) < 0)
+               return -1;
+
+       /* send offset of data */
+       control = MV64x60_I2C_CONTROL_TWSIEN;
+       status = MV64x60_I2C_STATUS_MAST_WR_ACK;
+       if (offset_size > 1) {
+               if (mv64x60_i2c_write_byte(offset >> 8, control, status) < 0)
+                       return -1;
+       }
+       if (mv64x60_i2c_write_byte(offset, control, status) < 0)
+               return -1;
+
+       /* resend start */
+       control = MV64x60_I2C_CONTROL_START | MV64x60_I2C_CONTROL_TWSIEN;
+       status = MV64x60_I2C_STATUS_MAST_REPEAT_START;
+       if (mv64x60_i2c_control(control, status) < 0)
+               return -1;
+
+       /* select device for reading */
+       data = devaddr | 0x1;
+       control = MV64x60_I2C_CONTROL_TWSIEN;
+       status = MV64x60_I2C_STATUS_MAST_RD_ADDR_ACK;
+       if (mv64x60_i2c_write_byte(data, control, status) < 0)
+               return -1;
+
+       /* read all but last byte of data */
+       control = MV64x60_I2C_CONTROL_ACK | MV64x60_I2C_CONTROL_TWSIEN;
+       status = MV64x60_I2C_STATUS_MAST_RD_DATA_ACK;
+
+       for (i=1; i<count; i++) {
+               data = mv64x60_i2c_read_byte(control, status);
+               if (data < 0) {
+                       printf("errors on iteration %d\n", i);
+                       return -1;
+               }
+               *buf++ = data;
+       }
+
+       /* read last byte of data */
+       control = MV64x60_I2C_CONTROL_TWSIEN;
+       status = MV64x60_I2C_STATUS_MAST_RD_DATA_NO_ACK;
+       data = mv64x60_i2c_read_byte(control, status);
+       if (data < 0)
+               return -1;
+       *buf++ = data;
+
+       /* send stop */
+       control = MV64x60_I2C_CONTROL_STOP | MV64x60_I2C_CONTROL_TWSIEN;
+       status = MV64x60_I2C_STATUS_NO_STATUS;
+       if (mv64x60_i2c_control(control, status) < 0)
+               return -1;
+
+       return count;
+}
+
+int mv64x60_i2c_open(void)
+{
+       u32 v;
+       void *devp;
+
+       devp = finddevice("/mv64x60/i2c");
+       if (devp == NULL)
+               goto err_out;
+       if (getprop(devp, "virtual-reg", &v, sizeof(v)) != sizeof(v))
+               goto err_out;
+
+       ctlr_base = (u8 *)v;
+       return 0;
+
+err_out:
+       return -1;
+}
+
+void mv64x60_i2c_close(void)
+{
+       ctlr_base = NULL;
+}