lin
2025-03-21 c2c82c91f6acd44c57766034b6ced0c53c164a55
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
/*
 * 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"
 
/* 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 = find_node_by_compatible(NULL, "marvell,mv64360-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;
}