[POWERPC] Fix console output getting dropped on platforms without udbg_putc
[linux-2.6] / arch / powerpc / boot / mv64x60_i2c.c
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
24 extern 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
60 static u8 *ctlr_base;
61
62 static 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
77 static 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
83 static 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
91 static 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
98 int 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
185 int 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
199 err_out:
200         return -1;
201 }
202
203 void mv64x60_i2c_close(void)
204 {
205         ctlr_base = NULL;
206 }