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