summaryrefslogtreecommitdiff
path: root/board/keymile/common/common.c
blob: 0f57015a5e52079a40a245607aca818a134f9681 (plain)
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
205
206
207
208
209
/*
 * (C) Copyright 2008
 * Heiko Schocher, DENX Software Engineering, hs@denx.de.
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <mpc8260.h>
#include <ioports.h>
#include <malloc.h>

#if defined(CONFIG_OF_BOARD_SETUP) && defined(CONFIG_OF_LIBFDT)
#include <libfdt.h>
#endif

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
#include <i2c.h>
#endif

extern int i2c_soft_read_pin (void);

#if defined(CFG_I2C_INIT_BOARD)
#define DELAY_ABORT_SEQ		62
#define DELAY_HALF_PERIOD	(500 / (CFG_I2C_SPEED / 1000))

#if defined(CONFIG_MGCOGE)
#define SDA_MASK	0x00010000
#define SCL_MASK	0x00020000
static void set_pin (int state, unsigned long mask)
{
	volatile ioport_t *iop = ioport_addr ((immap_t *)CFG_IMMR, 3);

	if (state)
		iop->pdat |= (mask);
	else
		iop->pdat &= ~(mask);

	iop->pdir |= (mask);
}

static int get_pin (unsigned long mask)
{
	volatile ioport_t *iop = ioport_addr ((immap_t *)CFG_IMMR, 3);

	iop->pdir &= ~(mask);
	return (0 != (iop->pdat & (mask)));
}

static void set_sda (int state)
{
	set_pin (state, SDA_MASK);
}

static void set_scl (int state)
{
	set_pin (state, SCL_MASK);
}

static int get_sda (void)
{
	return get_pin (SDA_MASK);
}

static int get_scl (void)
{
	return get_pin (SCL_MASK);
}

#if defined(CONFIG_HARD_I2C)
static void setports (int gpio)
{
	volatile ioport_t *iop = ioport_addr ((immap_t *)CFG_IMMR, 3);

	if (gpio) {
		iop->ppar &= ~(SDA_MASK | SCL_MASK);
		iop->podr &= ~(SDA_MASK | SCL_MASK);
	} else {
		iop->ppar |= (SDA_MASK | SCL_MASK);
		iop->pdir &= ~(SDA_MASK | SCL_MASK);
		iop->podr |= (SDA_MASK | SCL_MASK);
	}
}
#endif
#endif

#if defined(CONFIG_MGSUVD)
static void set_sda (int state)
{
	I2C_SDA(state);
}

static void set_scl (int state)
{
	I2C_SCL(state);
}

static int get_sda (void)
{
	return i2c_soft_read_pin ();
}

static int get_scl (void)
{
	int	val;

	*(unsigned short *)(I2C_BASE_DIR) &=  ~SCL_CONF;
	udelay (1);
	val = *(unsigned char *)(I2C_BASE_PORT);

	return ((val & SCL_BIT) == SCL_BIT);
}

#endif

static void writeStartSeq (void)
{
	set_sda (1);
	udelay (DELAY_HALF_PERIOD);
	set_scl (1);
	udelay (DELAY_HALF_PERIOD);
	set_sda (0);
	udelay (DELAY_HALF_PERIOD);
	set_scl (0);
	udelay (DELAY_HALF_PERIOD);
}

/* I2C is a synchronous protocol and resets of the processor in the middle
   of an access can block the I2C Bus until a powerdown of the full unit is
   done. This function toggles the SCL until the SCL and SCA line are
   released, but max. 16 times, after this a I2C start-sequence is sent.
   This I2C Deblocking mechanism was developed by Keymile in association
   with Anatech and Atmel in 1998.
 */
static int i2c_make_abort (void)
{
	int	scl_state = 0;
	int	sda_state = 0;
	int	i = 0;
	int	ret = 0;

	if (!get_sda ()) {
		ret = -1;
		while (i < 16) {
			i++;
			set_scl (0);
			udelay (DELAY_ABORT_SEQ);
			set_scl (1);
			udelay (DELAY_ABORT_SEQ);
			scl_state = get_scl ();
			sda_state = get_sda ();
			if (scl_state && sda_state) {
				ret = 0;
				break;
			}
		}
	}
	if (ret == 0) {
		for (i =0; i < 5; i++) {
			writeStartSeq ();
		}
	}
	get_sda ();
	return ret;
}

/**
 * i2c_init_board - reset i2c bus. When the board is powercycled during a
 * bus transfer it might hang; for details see doc/I2C_Edge_Conditions.
 */
void i2c_init_board(void)
{
#if defined(CONFIG_HARD_I2C)
	volatile immap_t *immap = (immap_t *)CFG_IMMR ;
	volatile i2c8260_t *i2c	= (i2c8260_t *)&immap->im_i2c;

	/* disable I2C controller first, otherwhise it thinks we want to    */
	/* talk to the slave port...                                        */
	i2c->i2c_i2mod &= ~0x01;

	/* Set the PortPins to GPIO */
	setports (1);
#endif

	/* Now run the AbortSequence() */
	i2c_make_abort ();

#if defined(CONFIG_HARD_I2C)
	/* Set the PortPins back to use for I2C */
	setports (0);
#endif
}
#endif