]>
Commit | Line | Data |
---|---|---|
b825f158 | 1 | /* |
2 | * (C) Copyright 2004 Tundra Semiconductor Corp. | |
3 | * Author: Alex Bounine | |
4 | * | |
5 | * See file CREDITS for list of people who contributed to this | |
6 | * project. | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License as | |
10 | * published by the Free Software Foundation; either version 2 of | |
11 | * the License, or (at your option) any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
21 | * MA 02111-1307 USA | |
22 | * | |
23 | */ | |
24 | ||
25 | #include <config.h> | |
26 | ||
27 | #ifdef CONFIG_TSI108_I2C | |
28 | ||
29 | #include <common.h> | |
30 | #include <tsi108.h> | |
31 | ||
32 | #if (CONFIG_COMMANDS & CFG_CMD_I2C) | |
33 | ||
34 | #define I2C_DELAY 100000 | |
35 | #undef DEBUG_I2C | |
36 | ||
37 | #ifdef DEBUG_I2C | |
38 | #define DPRINT(x) printf(x) | |
39 | #else | |
40 | #define DPRINT(x) | |
41 | #endif | |
42 | ||
43 | /* All functions assume that Tsi108 I2C block is the only master on the bus */ | |
44 | /* I2C read helper function */ | |
45 | ||
46 | static int i2c_read_byte( | |
47 | uint i2c_chan, /* I2C channel number: 0 - main, 1 - SDC SPD */ | |
48 | uchar chip_addr,/* I2C device address on the bus */ | |
49 | uint byte_addr, /* Byte address within I2C device */ | |
50 | uchar * buffer /* pointer to data buffer */ | |
51 | ) | |
52 | { | |
53 | u32 temp; | |
54 | u32 to_count = I2C_DELAY; | |
55 | u32 op_status = TSI108_I2C_TIMEOUT_ERR; | |
56 | u32 chan_offset = TSI108_I2C_OFFSET; | |
57 | ||
58 | DPRINT(("I2C read_byte() %d 0x%02x 0x%02x\n", | |
59 | i2c_chan, chip_addr, byte_addr)); | |
60 | ||
61 | if (0 != i2c_chan) { | |
62 | chan_offset = TSI108_I2C_SDRAM_OFFSET; | |
63 | } | |
64 | ||
65 | /* Check if I2C operation is in progress */ | |
66 | temp = *(u32 *) (CFG_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2); | |
67 | ||
68 | if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | | |
69 | I2C_CNTRL2_START)) | |
70 | ) { | |
71 | /* Set device address and operation (read = 0) */ | |
72 | temp = (byte_addr << 16) | ((chip_addr & 0x07) << 8) | | |
73 | ((chip_addr >> 3) & 0x0F); | |
74 | *(u32 *) (CFG_TSI108_CSR_BASE + chan_offset + I2C_CNTRL1) = | |
75 | temp; | |
76 | ||
77 | /* Issue the read command | |
78 | * (at this moment all other parameters are 0 | |
79 | * (size = 1 byte, lane = 0) | |
80 | */ | |
81 | ||
82 | *(u32 *) (CFG_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2) = | |
83 | (I2C_CNTRL2_START); | |
84 | ||
85 | /* Wait until operation completed */ | |
86 | do { | |
87 | /* Read I2C operation status */ | |
88 | temp = | |
89 | *(u32 *) (CFG_TSI108_CSR_BASE + chan_offset + | |
90 | I2C_CNTRL2); | |
91 | ||
92 | if (0 == | |
93 | (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_START))) | |
94 | { | |
95 | if (0 == | |
96 | (temp & | |
97 | (I2C_CNTRL2_I2C_CFGERR | | |
98 | I2C_CNTRL2_I2C_TO_ERR)) | |
99 | ) { | |
100 | op_status = TSI108_I2C_SUCCESS; | |
101 | ||
102 | temp = *(u32 *) (CFG_TSI108_CSR_BASE + | |
103 | chan_offset + | |
104 | I2C_RD_DATA); | |
105 | ||
106 | *buffer = (u8) (temp & 0xFF); | |
107 | } else { | |
108 | /* report HW error */ | |
109 | op_status = TSI108_I2C_IF_ERROR; | |
110 | ||
111 | DPRINT(("I2C HW error reported: 0x%02x\n", temp)); | |
112 | } | |
113 | ||
114 | break; | |
115 | } | |
116 | } while (to_count--); | |
117 | } else { | |
118 | op_status = TSI108_I2C_IF_BUSY; | |
119 | ||
120 | DPRINT(("I2C Transaction start failed: 0x%02x\n", temp)); | |
121 | } | |
122 | ||
123 | DPRINT(("I2C read_byte() status: 0x%02x\n", op_status)); | |
124 | return op_status; | |
125 | } | |
126 | ||
127 | /* | |
128 | * I2C Read interface as defined in "include/i2c.h" : | |
129 | * chip_addr: I2C chip address, range 0..127 | |
130 | * (to read from SPD channel EEPROM use (0xD0 ... 0xD7) | |
131 | * NOTE: The bit 7 in the chip_addr serves as a channel select. | |
132 | * This hack is for enabling "isdram" command on Tsi108 boards | |
133 | * without changes to common code. Used for I2C reads only. | |
134 | * byte_addr: Memory or register address within the chip | |
135 | * alen: Number of bytes to use for addr (typically 1, 2 for larger | |
136 | * memories, 0 for register type devices with only one | |
137 | * register) | |
138 | * buffer: Pointer to destination buffer for data to be read | |
139 | * len: How many bytes to read | |
140 | * | |
141 | * Returns: 0 on success, not 0 on failure | |
142 | */ | |
143 | ||
144 | int i2c_read(uchar chip_addr, uint byte_addr, int alen, uchar * buffer, int len) | |
145 | { | |
146 | u32 op_status = TSI108_I2C_PARAM_ERR; | |
147 | u32 i2c_if = 0; | |
148 | ||
149 | /* Hack to support second (SPD) I2C controller (SPD EEPROM read only).*/ | |
150 | if (0xD0 == (chip_addr & ~0x07)) { | |
151 | i2c_if = 1; | |
152 | chip_addr &= 0x7F; | |
153 | } | |
154 | /* Check for valid I2C address */ | |
155 | if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { | |
156 | while (len--) { | |
157 | op_status = | |
158 | i2c_read_byte(i2c_if, chip_addr, byte_addr++, | |
159 | buffer++); | |
160 | ||
161 | if (TSI108_I2C_SUCCESS != op_status) { | |
162 | DPRINT(("I2C read_byte() failed: 0x%02x (%d left)\n", op_status, len)); | |
163 | ||
164 | break; | |
165 | } | |
166 | } | |
167 | } | |
168 | ||
169 | DPRINT(("I2C read() status: 0x%02x\n", op_status)); | |
170 | return op_status; | |
171 | } | |
172 | ||
173 | /* I2C write helper function */ | |
174 | ||
175 | static int i2c_write_byte(uchar chip_addr,/* I2C device address on the bus */ | |
176 | uint byte_addr, /* Byte address within I2C device */ | |
177 | uchar * buffer /* pointer to data buffer */ | |
178 | ) | |
179 | { | |
180 | u32 temp; | |
181 | u32 to_count = I2C_DELAY; | |
182 | u32 op_status = TSI108_I2C_TIMEOUT_ERR; | |
183 | ||
184 | /* Check if I2C operation is in progress */ | |
185 | temp = *(u32 *) (CFG_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2); | |
186 | ||
187 | if (0 == | |
188 | (temp & | |
189 | (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) | |
190 | { | |
191 | /* Place data into the I2C Tx Register */ | |
192 | *(u32 *) (CFG_TSI108_CSR_BASE + TSI108_I2C_OFFSET + | |
193 | I2C_TX_DATA) = (u32) * buffer; | |
194 | ||
195 | /* Set device address and operation */ | |
196 | temp = | |
197 | I2C_CNTRL1_I2CWRITE | (byte_addr << 16) | | |
198 | ((chip_addr & 0x07) << 8) | ((chip_addr >> 3) & 0x0F); | |
199 | *(u32 *) (CFG_TSI108_CSR_BASE + TSI108_I2C_OFFSET + | |
200 | I2C_CNTRL1) = temp; | |
201 | ||
202 | /* Issue the write command (at this moment all other parameters | |
203 | * are 0 (size = 1 byte, lane = 0) | |
204 | */ | |
205 | ||
206 | *(u32 *) (CFG_TSI108_CSR_BASE + TSI108_I2C_OFFSET + | |
207 | I2C_CNTRL2) = (I2C_CNTRL2_START); | |
208 | ||
209 | op_status = TSI108_I2C_TIMEOUT_ERR; | |
210 | ||
211 | /* Wait until operation completed */ | |
212 | do { | |
213 | // Read I2C operation status | |
214 | temp = | |
215 | *(u32 *) (CFG_TSI108_CSR_BASE + TSI108_I2C_OFFSET + | |
216 | I2C_CNTRL2); | |
217 | ||
218 | if (0 == | |
219 | (temp & (I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) | |
220 | { | |
221 | if (0 == | |
222 | (temp & | |
223 | (I2C_CNTRL2_I2C_CFGERR | | |
224 | I2C_CNTRL2_I2C_TO_ERR))) { | |
225 | op_status = TSI108_I2C_SUCCESS; | |
226 | } else { | |
227 | /* report detected HW error */ | |
228 | op_status = TSI108_I2C_IF_ERROR; | |
229 | ||
230 | DPRINT(("I2C HW error reported: 0x%02x\n", temp)); | |
231 | } | |
232 | ||
233 | break; | |
234 | } | |
235 | ||
236 | } while (to_count--); | |
237 | } else { | |
238 | op_status = TSI108_I2C_IF_BUSY; | |
239 | ||
240 | DPRINT(("I2C Transaction start failed: 0x%02x\n", temp)); | |
241 | } | |
242 | ||
243 | return op_status; | |
244 | } | |
245 | ||
246 | /* | |
247 | * I2C Write interface as defined in "include/i2c.h" : | |
248 | * chip_addr: I2C chip address, range 0..127 | |
249 | * byte_addr: Memory or register address within the chip | |
250 | * alen: Number of bytes to use for addr (typically 1, 2 for larger | |
251 | * memories, 0 for register type devices with only one | |
252 | * register) | |
253 | * buffer: Pointer to data to be written | |
254 | * len: How many bytes to write | |
255 | * | |
256 | * Returns: 0 on success, not 0 on failure | |
257 | */ | |
258 | ||
259 | int i2c_write(uchar chip_addr, uint byte_addr, int alen, uchar * buffer, | |
260 | int len) | |
261 | { | |
262 | u32 op_status = TSI108_I2C_PARAM_ERR; | |
263 | ||
264 | /* Check for valid I2C address */ | |
265 | if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { | |
266 | while (len--) { | |
267 | op_status = | |
268 | i2c_write_byte(chip_addr, byte_addr++, buffer++); | |
269 | ||
270 | if (TSI108_I2C_SUCCESS != op_status) { | |
271 | DPRINT(("I2C write_byte() failed: 0x%02x (%d left)\n", op_status, len)); | |
272 | ||
273 | break; | |
274 | } | |
275 | } | |
276 | } | |
277 | ||
278 | return op_status; | |
279 | } | |
280 | ||
281 | /* | |
282 | * I2C interface function as defined in "include/i2c.h". | |
283 | * Probe the given I2C chip address by reading single byte from offset 0. | |
284 | * Returns 0 if a chip responded, not 0 on failure. | |
285 | */ | |
286 | ||
287 | int i2c_probe(uchar chip) | |
288 | { | |
289 | u32 tmp; | |
290 | ||
291 | /* | |
292 | * Try to read the first location of the chip. | |
293 | * The Tsi108 HW doesn't support sending just the chip address | |
294 | * and checkong for an <ACK> back. | |
295 | */ | |
296 | return i2c_read(chip, 0, 1, (char *)&tmp, 1); | |
297 | } | |
298 | ||
299 | #endif /* (CONFIG_COMMANDS & CFG_CMD_I2C) */ | |
300 | #endif /* CONFIG_TSI108_I2C */ |