]>
Commit | Line | Data |
---|---|---|
b825f158 | 1 | /* |
2 | * (C) Copyright 2004 Tundra Semiconductor Corp. | |
3 | * Author: Alex Bounine | |
4 | * | |
1a459660 | 5 | * SPDX-License-Identifier: GPL-2.0+ |
b825f158 | 6 | */ |
7 | ||
8 | #include <config.h> | |
ee311214 | 9 | #include <common.h> |
b825f158 | 10 | |
b825f158 | 11 | #include <tsi108.h> |
12 | ||
cb51c0bf | 13 | #if defined(CONFIG_CMD_I2C) |
b825f158 | 14 | |
ee311214 | 15 | #define I2C_DELAY 100000 |
b825f158 | 16 | #undef DEBUG_I2C |
17 | ||
18 | #ifdef DEBUG_I2C | |
ee311214 | 19 | #define DPRINT(x) printf (x) |
b825f158 | 20 | #else |
21 | #define DPRINT(x) | |
22 | #endif | |
23 | ||
24 | /* All functions assume that Tsi108 I2C block is the only master on the bus */ | |
25 | /* I2C read helper function */ | |
26 | ||
f0722ee7 PT |
27 | void i2c_init(int speed, int slaveaddr) |
28 | { | |
29 | /* | |
30 | * The TSI108 has a fixed I2C clock rate and doesn't support slave | |
31 | * operation. This function only exists as a stub to fit into the | |
32 | * U-Boot I2C API. | |
33 | */ | |
34 | } | |
35 | ||
ee311214 | 36 | static int i2c_read_byte ( |
b825f158 | 37 | uint i2c_chan, /* I2C channel number: 0 - main, 1 - SDC SPD */ |
38 | uchar chip_addr,/* I2C device address on the bus */ | |
39 | uint byte_addr, /* Byte address within I2C device */ | |
40 | uchar * buffer /* pointer to data buffer */ | |
41 | ) | |
42 | { | |
43 | u32 temp; | |
44 | u32 to_count = I2C_DELAY; | |
45 | u32 op_status = TSI108_I2C_TIMEOUT_ERR; | |
46 | u32 chan_offset = TSI108_I2C_OFFSET; | |
47 | ||
ee311214 | 48 | DPRINT (("I2C read_byte() %d 0x%02x 0x%02x\n", |
b825f158 | 49 | i2c_chan, chip_addr, byte_addr)); |
50 | ||
ee311214 | 51 | if (0 != i2c_chan) |
b825f158 | 52 | chan_offset = TSI108_I2C_SDRAM_OFFSET; |
b825f158 | 53 | |
54 | /* Check if I2C operation is in progress */ | |
6d0f6bcf | 55 | temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2); |
b825f158 | 56 | |
57 | if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | | |
ee311214 | 58 | I2C_CNTRL2_START))) { |
b825f158 | 59 | /* Set device address and operation (read = 0) */ |
60 | temp = (byte_addr << 16) | ((chip_addr & 0x07) << 8) | | |
61 | ((chip_addr >> 3) & 0x0F); | |
6d0f6bcf | 62 | *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL1) = |
b825f158 | 63 | temp; |
64 | ||
65 | /* Issue the read command | |
ee311214 | 66 | * (at this moment all other parameters are 0 |
b825f158 | 67 | * (size = 1 byte, lane = 0) |
68 | */ | |
69 | ||
6d0f6bcf | 70 | *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2) = |
b825f158 | 71 | (I2C_CNTRL2_START); |
72 | ||
73 | /* Wait until operation completed */ | |
74 | do { | |
75 | /* Read I2C operation status */ | |
6d0f6bcf | 76 | temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2); |
647d3c3e WD |
77 | |
78 | if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_START))) { | |
79 | if (0 == (temp & | |
b825f158 | 80 | (I2C_CNTRL2_I2C_CFGERR | |
81 | I2C_CNTRL2_I2C_TO_ERR)) | |
82 | ) { | |
83 | op_status = TSI108_I2C_SUCCESS; | |
84 | ||
6d0f6bcf | 85 | temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + |
b825f158 | 86 | chan_offset + |
87 | I2C_RD_DATA); | |
88 | ||
89 | *buffer = (u8) (temp & 0xFF); | |
90 | } else { | |
91 | /* report HW error */ | |
92 | op_status = TSI108_I2C_IF_ERROR; | |
93 | ||
ee311214 | 94 | DPRINT (("I2C HW error reported: 0x%02x\n", temp)); |
b825f158 | 95 | } |
96 | ||
97 | break; | |
98 | } | |
99 | } while (to_count--); | |
100 | } else { | |
101 | op_status = TSI108_I2C_IF_BUSY; | |
102 | ||
ee311214 | 103 | DPRINT (("I2C Transaction start failed: 0x%02x\n", temp)); |
b825f158 | 104 | } |
105 | ||
ee311214 | 106 | DPRINT (("I2C read_byte() status: 0x%02x\n", op_status)); |
b825f158 | 107 | return op_status; |
108 | } | |
109 | ||
ee311214 | 110 | /* |
b825f158 | 111 | * I2C Read interface as defined in "include/i2c.h" : |
112 | * chip_addr: I2C chip address, range 0..127 | |
113 | * (to read from SPD channel EEPROM use (0xD0 ... 0xD7) | |
114 | * NOTE: The bit 7 in the chip_addr serves as a channel select. | |
0f89c54b | 115 | * This hack is for enabling "i2c sdram" command on Tsi108 boards |
ee311214 | 116 | * without changes to common code. Used for I2C reads only. |
b825f158 | 117 | * byte_addr: Memory or register address within the chip |
118 | * alen: Number of bytes to use for addr (typically 1, 2 for larger | |
119 | * memories, 0 for register type devices with only one | |
120 | * register) | |
121 | * buffer: Pointer to destination buffer for data to be read | |
122 | * len: How many bytes to read | |
123 | * | |
124 | * Returns: 0 on success, not 0 on failure | |
125 | */ | |
126 | ||
ee311214 | 127 | int i2c_read (uchar chip_addr, uint byte_addr, int alen, |
128 | uchar * buffer, int len) | |
b825f158 | 129 | { |
130 | u32 op_status = TSI108_I2C_PARAM_ERR; | |
131 | u32 i2c_if = 0; | |
132 | ||
133 | /* Hack to support second (SPD) I2C controller (SPD EEPROM read only).*/ | |
134 | if (0xD0 == (chip_addr & ~0x07)) { | |
135 | i2c_if = 1; | |
136 | chip_addr &= 0x7F; | |
137 | } | |
138 | /* Check for valid I2C address */ | |
139 | if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { | |
140 | while (len--) { | |
647d3c3e | 141 | op_status = i2c_read_byte(i2c_if, chip_addr, byte_addr++, buffer++); |
b825f158 | 142 | |
143 | if (TSI108_I2C_SUCCESS != op_status) { | |
ee311214 | 144 | DPRINT (("I2C read_byte() failed: 0x%02x (%d left)\n", op_status, len)); |
b825f158 | 145 | |
146 | break; | |
147 | } | |
148 | } | |
149 | } | |
150 | ||
ee311214 | 151 | DPRINT (("I2C read() status: 0x%02x\n", op_status)); |
b825f158 | 152 | return op_status; |
153 | } | |
154 | ||
155 | /* I2C write helper function */ | |
156 | ||
ee311214 | 157 | static int i2c_write_byte (uchar chip_addr,/* I2C device address on the bus */ |
b825f158 | 158 | uint byte_addr, /* Byte address within I2C device */ |
159 | uchar * buffer /* pointer to data buffer */ | |
160 | ) | |
161 | { | |
162 | u32 temp; | |
163 | u32 to_count = I2C_DELAY; | |
164 | u32 op_status = TSI108_I2C_TIMEOUT_ERR; | |
165 | ||
166 | /* Check if I2C operation is in progress */ | |
6d0f6bcf | 167 | temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2); |
b825f158 | 168 | |
647d3c3e | 169 | if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) { |
b825f158 | 170 | /* Place data into the I2C Tx Register */ |
6d0f6bcf | 171 | *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + |
b825f158 | 172 | I2C_TX_DATA) = (u32) * buffer; |
173 | ||
174 | /* Set device address and operation */ | |
175 | temp = | |
176 | I2C_CNTRL1_I2CWRITE | (byte_addr << 16) | | |
177 | ((chip_addr & 0x07) << 8) | ((chip_addr >> 3) & 0x0F); | |
6d0f6bcf | 178 | *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + |
b825f158 | 179 | I2C_CNTRL1) = temp; |
180 | ||
181 | /* Issue the write command (at this moment all other parameters | |
182 | * are 0 (size = 1 byte, lane = 0) | |
183 | */ | |
647d3c3e | 184 | |
6d0f6bcf | 185 | *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + |
b825f158 | 186 | I2C_CNTRL2) = (I2C_CNTRL2_START); |
187 | ||
188 | op_status = TSI108_I2C_TIMEOUT_ERR; | |
189 | ||
190 | /* Wait until operation completed */ | |
191 | do { | |
ee311214 | 192 | /* Read I2C operation status */ |
6d0f6bcf | 193 | temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2); |
647d3c3e WD |
194 | |
195 | if (0 == (temp & (I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) { | |
196 | if (0 == (temp & | |
b825f158 | 197 | (I2C_CNTRL2_I2C_CFGERR | |
198 | I2C_CNTRL2_I2C_TO_ERR))) { | |
199 | op_status = TSI108_I2C_SUCCESS; | |
200 | } else { | |
201 | /* report detected HW error */ | |
202 | op_status = TSI108_I2C_IF_ERROR; | |
203 | ||
ee311214 | 204 | DPRINT (("I2C HW error reported: 0x%02x\n", temp)); |
b825f158 | 205 | } |
206 | ||
207 | break; | |
208 | } | |
209 | ||
210 | } while (to_count--); | |
211 | } else { | |
212 | op_status = TSI108_I2C_IF_BUSY; | |
213 | ||
ee311214 | 214 | DPRINT (("I2C Transaction start failed: 0x%02x\n", temp)); |
b825f158 | 215 | } |
216 | ||
217 | return op_status; | |
218 | } | |
219 | ||
ee311214 | 220 | /* |
b825f158 | 221 | * I2C Write interface as defined in "include/i2c.h" : |
222 | * chip_addr: I2C chip address, range 0..127 | |
223 | * byte_addr: Memory or register address within the chip | |
224 | * alen: Number of bytes to use for addr (typically 1, 2 for larger | |
225 | * memories, 0 for register type devices with only one | |
226 | * register) | |
227 | * buffer: Pointer to data to be written | |
228 | * len: How many bytes to write | |
229 | * | |
230 | * Returns: 0 on success, not 0 on failure | |
231 | */ | |
232 | ||
ee311214 | 233 | int i2c_write (uchar chip_addr, uint byte_addr, int alen, uchar * buffer, |
b825f158 | 234 | int len) |
235 | { | |
236 | u32 op_status = TSI108_I2C_PARAM_ERR; | |
237 | ||
238 | /* Check for valid I2C address */ | |
239 | if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { | |
240 | while (len--) { | |
241 | op_status = | |
ee311214 | 242 | i2c_write_byte (chip_addr, byte_addr++, buffer++); |
b825f158 | 243 | |
244 | if (TSI108_I2C_SUCCESS != op_status) { | |
ee311214 | 245 | DPRINT (("I2C write_byte() failed: 0x%02x (%d left)\n", op_status, len)); |
b825f158 | 246 | |
247 | break; | |
248 | } | |
249 | } | |
250 | } | |
251 | ||
252 | return op_status; | |
253 | } | |
254 | ||
ee311214 | 255 | /* |
b825f158 | 256 | * I2C interface function as defined in "include/i2c.h". |
257 | * Probe the given I2C chip address by reading single byte from offset 0. | |
258 | * Returns 0 if a chip responded, not 0 on failure. | |
259 | */ | |
260 | ||
ee311214 | 261 | int i2c_probe (uchar chip) |
b825f158 | 262 | { |
263 | u32 tmp; | |
264 | ||
265 | /* | |
266 | * Try to read the first location of the chip. | |
267 | * The Tsi108 HW doesn't support sending just the chip address | |
268 | * and checkong for an <ACK> back. | |
269 | */ | |
409ecdc0 | 270 | return i2c_read (chip, 0, 1, (uchar *)&tmp, 1); |
b825f158 | 271 | } |
272 | ||
cb51c0bf | 273 | #endif |