mgcoge, mgsuvd: add board specific I2C deblocking mechanism.
[pandora-u-boot.git] / board / keymile / common / common.c
1 /*
2  * (C) Copyright 2008
3  * Heiko Schocher, DENX Software Engineering, hs@denx.de.
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 #include <common.h>
25 #include <mpc8260.h>
26 #include <ioports.h>
27 #include <malloc.h>
28
29 #if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_OF_LIBFDT)
30 #include <libfdt.h>
31 #endif
32
33 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
34 #include <i2c.h>
35 #endif
36
37 extern int i2c_soft_read_pin (void);
38
39 #if defined(CFG_I2C_INIT_BOARD)
40 #define DELAY_ABORT_SEQ         62
41 #define DELAY_HALF_PERIOD       (500 / (CFG_I2C_SPEED / 1000))
42
43 #if defined(CONFIG_MGCOGE)
44 #define SDA_MASK        0x00010000
45 #define SCL_MASK        0x00020000
46 static void set_pin (int state, unsigned long mask)
47 {
48         volatile ioport_t *iop = ioport_addr ((immap_t *)CFG_IMMR, 3);
49
50         if (state)
51                 iop->pdat |= (mask);
52         else
53                 iop->pdat &= ~(mask);
54
55         iop->pdir |= (mask);
56 }
57
58 static int get_pin (unsigned long mask)
59 {
60         volatile ioport_t *iop = ioport_addr ((immap_t *)CFG_IMMR, 3);
61
62         iop->pdir &= ~(mask);
63         return (0 != (iop->pdat & (mask)));
64 }
65
66 static void set_sda (int state)
67 {
68         set_pin (state, SDA_MASK);
69 }
70
71 static void set_scl (int state)
72 {
73         set_pin (state, SCL_MASK);
74 }
75
76 static int get_sda (void)
77 {
78         return get_pin (SDA_MASK);
79 }
80
81 static int get_scl (void)
82 {
83         return get_pin (SCL_MASK);
84 }
85
86 #if defined(CONFIG_HARD_I2C)
87 static void setports (int gpio)
88 {
89         volatile ioport_t *iop = ioport_addr ((immap_t *)CFG_IMMR, 3);
90
91         if (gpio) {
92                 iop->ppar &= ~(SDA_MASK | SCL_MASK);
93                 iop->podr &= ~(SDA_MASK | SCL_MASK);
94         } else {
95                 iop->ppar |= (SDA_MASK | SCL_MASK);
96                 iop->pdir &= ~(SDA_MASK | SCL_MASK);
97                 iop->podr |= (SDA_MASK | SCL_MASK);
98         }
99 }
100 #endif
101 #endif
102
103 #if defined(CONFIG_MGSUVD)
104 static void set_sda (int state)
105 {
106         I2C_SDA(state);
107 }
108
109 static void set_scl (int state)
110 {
111         I2C_SCL(state);
112 }
113
114 static int get_sda (void)
115 {
116         return i2c_soft_read_pin ();
117 }
118
119 static int get_scl (void)
120 {
121         int     val;
122
123         *(unsigned short *)(I2C_BASE_DIR) &=  ~SCL_CONF;
124         udelay (1);
125         val = *(unsigned char *)(I2C_BASE_PORT);
126
127         return ((val & SCL_BIT) == SCL_BIT);
128 }
129
130 #endif
131
132 static void writeStartSeq (void)
133 {
134         set_sda (1);
135         udelay (DELAY_HALF_PERIOD);
136         set_scl (1);
137         udelay (DELAY_HALF_PERIOD);
138         set_sda (0);
139         udelay (DELAY_HALF_PERIOD);
140         set_scl (0);
141         udelay (DELAY_HALF_PERIOD);
142 }
143
144 /* I2C is a synchronous protocol and resets of the processor in the middle
145    of an access can block the I2C Bus until a powerdown of the full unit is
146    done. This function toggles the SCL until the SCL and SCA line are
147    released, but max. 16 times, after this a I2C start-sequence is sent.
148    This I2C Deblocking mechanism was developed by Keymile in association
149    with Anatech and Atmel in 1998.
150  */
151 static int i2c_make_abort (void)
152 {
153         int     scl_state = 0;
154         int     sda_state = 0;
155         int     i = 0;
156         int     ret = 0;
157
158         if (!get_sda ()) {
159                 ret = -1;
160                 while (i < 16) {
161                         i++;
162                         set_scl (0);
163                         udelay (DELAY_ABORT_SEQ);
164                         set_scl (1);
165                         udelay (DELAY_ABORT_SEQ);
166                         scl_state = get_scl ();
167                         sda_state = get_sda ();
168                         if (scl_state && sda_state) {
169                                 ret = 0;
170                                 break;
171                         }
172                 }
173         }
174         if (ret == 0) {
175                 for (i =0; i < 5; i++) {
176                         writeStartSeq ();
177                 }
178         }
179         get_sda ();
180         return ret;
181 }
182
183 /**
184  * i2c_init_board - reset i2c bus. When the board is powercycled during a
185  * bus transfer it might hang; for details see doc/I2C_Edge_Conditions.
186  */
187 void i2c_init_board(void)
188 {
189 #if defined(CONFIG_HARD_I2C)
190         volatile immap_t *immap = (immap_t *)CFG_IMMR ;
191         volatile i2c8260_t *i2c = (i2c8260_t *)&immap->im_i2c;
192
193         /* disable I2C controller first, otherwhise it thinks we want to    */
194         /* talk to the slave port...                                        */
195         i2c->i2c_i2mod &= ~0x01;
196
197         /* Set the PortPins to GPIO */
198         setports (1);
199 #endif
200
201         /* Now run the AbortSequence() */
202         i2c_make_abort ();
203
204 #if defined(CONFIG_HARD_I2C)
205         /* Set the PortPins back to use for I2C */
206         setports (0);
207 #endif
208 }
209 #endif