aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/boot/mv64x60_i2c.c
diff options
context:
space:
mode:
authorMark A. Greer <mgreer@mvista.com>2007-05-11 20:54:53 -0400
committerPaul Mackerras <paulus@samba.org>2007-05-11 21:32:49 -0400
commitae4b3fbc7a91ea4e5685edb0310bb185a12e5943 (patch)
tree7891118b8de8be8703308e9c1f1b090dc0276bf2 /arch/powerpc/boot/mv64x60_i2c.c
parente12deb840ceed7051ab4799ae71b675a83c58c7c (diff)
[POWERPC] Add bootwrapper support for Marvell/mv64x60 I2C
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>
Diffstat (limited to 'arch/powerpc/boot/mv64x60_i2c.c')
-rw-r--r--arch/powerpc/boot/mv64x60_i2c.c206
1 files changed, 206 insertions, 0 deletions
diff --git a/arch/powerpc/boot/mv64x60_i2c.c b/arch/powerpc/boot/mv64x60_i2c.c
new file mode 100644
index 00000000000..435fe852868
--- /dev/null
+++ b/arch/powerpc/boot/mv64x60_i2c.c
@@ -0,0 +1,206 @@
1/*
2 * Bootloader version of the i2c driver for the MV64x60.
3 *
4 * Author: Dale Farnsworth <dfarnsworth@mvista.com>
5 * Maintained by: Mark A. Greer <mgreer@mvista.com>
6 *
7 * 2003, 2007 (c) MontaVista, Software, Inc. This file is licensed under
8 * the terms of the GNU General Public License version 2. This program is
9 * licensed "as is" without any warranty of any kind, whether express or
10 * implied.
11 */
12
13#include <stdarg.h>
14#include <stddef.h>
15#include "types.h"
16#include "elf.h"
17#include "page.h"
18#include "string.h"
19#include "stdio.h"
20#include "io.h"
21#include "ops.h"
22#include "mv64x60.h"
23
24extern void udelay(long);
25
26/* Register defines */
27#define MV64x60_I2C_REG_SLAVE_ADDR 0x00
28#define MV64x60_I2C_REG_DATA 0x04
29#define MV64x60_I2C_REG_CONTROL 0x08
30#define MV64x60_I2C_REG_STATUS 0x0c
31#define MV64x60_I2C_REG_BAUD 0x0c
32#define MV64x60_I2C_REG_EXT_SLAVE_ADDR 0x10
33#define MV64x60_I2C_REG_SOFT_RESET 0x1c
34
35#define MV64x60_I2C_CONTROL_ACK 0x04
36#define MV64x60_I2C_CONTROL_IFLG 0x08
37#define MV64x60_I2C_CONTROL_STOP 0x10
38#define MV64x60_I2C_CONTROL_START 0x20
39#define MV64x60_I2C_CONTROL_TWSIEN 0x40
40#define MV64x60_I2C_CONTROL_INTEN 0x80
41
42#define MV64x60_I2C_STATUS_BUS_ERR 0x00
43#define MV64x60_I2C_STATUS_MAST_START 0x08
44#define MV64x60_I2C_STATUS_MAST_REPEAT_START 0x10
45#define MV64x60_I2C_STATUS_MAST_WR_ADDR_ACK 0x18
46#define MV64x60_I2C_STATUS_MAST_WR_ADDR_NO_ACK 0x20
47#define MV64x60_I2C_STATUS_MAST_WR_ACK 0x28
48#define MV64x60_I2C_STATUS_MAST_WR_NO_ACK 0x30
49#define MV64x60_I2C_STATUS_MAST_LOST_ARB 0x38
50#define MV64x60_I2C_STATUS_MAST_RD_ADDR_ACK 0x40
51#define MV64x60_I2C_STATUS_MAST_RD_ADDR_NO_ACK 0x48
52#define MV64x60_I2C_STATUS_MAST_RD_DATA_ACK 0x50
53#define MV64x60_I2C_STATUS_MAST_RD_DATA_NO_ACK 0x58
54#define MV64x60_I2C_STATUS_MAST_WR_ADDR_2_ACK 0xd0
55#define MV64x60_I2C_STATUS_MAST_WR_ADDR_2_NO_ACK 0xd8
56#define MV64x60_I2C_STATUS_MAST_RD_ADDR_2_ACK 0xe0
57#define MV64x60_I2C_STATUS_MAST_RD_ADDR_2_NO_ACK 0xe8
58#define MV64x60_I2C_STATUS_NO_STATUS 0xf8
59
60static u8 *ctlr_base;
61
62static int mv64x60_i2c_wait_for_status(int wanted)
63{
64 int i;
65 int status;
66
67 for (i=0; i<1000; i++) {
68 udelay(10);
69 status = in_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_STATUS))
70 & 0xff;
71 if (status == wanted)
72 return status;
73 }
74 return -status;
75}
76
77static int mv64x60_i2c_control(int control, int status)
78{
79 out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_CONTROL), control & 0xff);
80 return mv64x60_i2c_wait_for_status(status);
81}
82
83static int mv64x60_i2c_read_byte(int control, int status)
84{
85 out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_CONTROL), control & 0xff);
86 if (mv64x60_i2c_wait_for_status(status) < 0)
87 return -1;
88 return in_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_DATA)) & 0xff;
89}
90
91static int mv64x60_i2c_write_byte(int data, int control, int status)
92{
93 out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_DATA), data & 0xff);
94 out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_CONTROL), control & 0xff);
95 return mv64x60_i2c_wait_for_status(status);
96}
97
98int mv64x60_i2c_read(u32 devaddr, u8 *buf, u32 offset, u32 offset_size,
99 u32 count)
100{
101 int i;
102 int data;
103 int control;
104 int status;
105
106 if (ctlr_base == NULL)
107 return -1;
108
109 /* send reset */
110 out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_SOFT_RESET), 0);
111 out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_SLAVE_ADDR), 0);
112 out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_EXT_SLAVE_ADDR), 0);
113 out_le32((u32 *)(ctlr_base + MV64x60_I2C_REG_BAUD), (4 << 3) | 0x4);
114
115 if (mv64x60_i2c_control(MV64x60_I2C_CONTROL_TWSIEN,
116 MV64x60_I2C_STATUS_NO_STATUS) < 0)
117 return -1;
118
119 /* send start */
120 control = MV64x60_I2C_CONTROL_START | MV64x60_I2C_CONTROL_TWSIEN;
121 status = MV64x60_I2C_STATUS_MAST_START;
122 if (mv64x60_i2c_control(control, status) < 0)
123 return -1;
124
125 /* select device for writing */
126 data = devaddr & ~0x1;
127 control = MV64x60_I2C_CONTROL_TWSIEN;
128 status = MV64x60_I2C_STATUS_MAST_WR_ADDR_ACK;
129 if (mv64x60_i2c_write_byte(data, control, status) < 0)
130 return -1;
131
132 /* send offset of data */
133 control = MV64x60_I2C_CONTROL_TWSIEN;
134 status = MV64x60_I2C_STATUS_MAST_WR_ACK;
135 if (offset_size > 1) {
136 if (mv64x60_i2c_write_byte(offset >> 8, control, status) < 0)
137 return -1;
138 }
139 if (mv64x60_i2c_write_byte(offset, control, status) < 0)
140 return -1;
141
142 /* resend start */
143 control = MV64x60_I2C_CONTROL_START | MV64x60_I2C_CONTROL_TWSIEN;
144 status = MV64x60_I2C_STATUS_MAST_REPEAT_START;
145 if (mv64x60_i2c_control(control, status) < 0)
146 return -1;
147
148 /* select device for reading */
149 data = devaddr | 0x1;
150 control = MV64x60_I2C_CONTROL_TWSIEN;
151 status = MV64x60_I2C_STATUS_MAST_RD_ADDR_ACK;
152 if (mv64x60_i2c_write_byte(data, control, status) < 0)
153 return -1;
154
155 /* read all but last byte of data */
156 control = MV64x60_I2C_CONTROL_ACK | MV64x60_I2C_CONTROL_TWSIEN;
157 status = MV64x60_I2C_STATUS_MAST_RD_DATA_ACK;
158
159 for (i=1; i<count; i++) {
160 data = mv64x60_i2c_read_byte(control, status);
161 if (data < 0) {
162 printf("errors on iteration %d\n", i);
163 return -1;
164 }
165 *buf++ = data;
166 }
167
168 /* read last byte of data */
169 control = MV64x60_I2C_CONTROL_TWSIEN;
170 status = MV64x60_I2C_STATUS_MAST_RD_DATA_NO_ACK;
171 data = mv64x60_i2c_read_byte(control, status);
172 if (data < 0)
173 return -1;
174 *buf++ = data;
175
176 /* send stop */
177 control = MV64x60_I2C_CONTROL_STOP | MV64x60_I2C_CONTROL_TWSIEN;
178 status = MV64x60_I2C_STATUS_NO_STATUS;
179 if (mv64x60_i2c_control(control, status) < 0)
180 return -1;
181
182 return count;
183}
184
185int mv64x60_i2c_open(void)
186{
187 u32 v;
188 void *devp;
189
190 devp = finddevice("/mv64x60/i2c");
191 if (devp == NULL)
192 goto err_out;
193 if (getprop(devp, "virtual-reg", &v, sizeof(v)) != sizeof(v))
194 goto err_out;
195
196 ctlr_base = (u8 *)v;
197 return 0;
198
199err_out:
200 return -1;
201}
202
203void mv64x60_i2c_close(void)
204{
205 ctlr_base = NULL;
206}