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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
|
/*
* L3 emulation using GPIO pins
*
* from the Linux sa1100-uda1341.c,
* Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
* Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License.
*
* Modified by Vita Nuova 2001
*
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
/*
* GPIO based L3 bus support.
*
* This provides control of Philips L3 type devices.
* GPIO lines are used for clock, data and mode pins.
*
* Note: The L3 pins are shared with I2C devices. This should not present
* any problems as long as an I2C start sequence is not generated. This is
* defined as a 1->0 transition on the data lines when the clock is high.
* It is critical this code only allow data transitions when the clock
* is low. This is always legal in L3.
*
* The IIC interface requires the clock and data pin to be LOW when idle. We
* must make sure we leave them in this state.
*
* It appears the read data is generated on the falling edge of the clock
* and should be held stable during the clock high time.
*/
/*
* L3 setup and hold times (expressed in us)
*/
enum {
L3DataSetupTime = 1, /* 190 ns */
L3DataHoldTime = 1, /* 30 ns */
L3ModeSetupTime = 1, /* 190 ns */
L3ModeHoldTime = 1, /* 190 ns */
L3ClockHighTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
L3ClockLowTime = 1, /* 250 ns (min is 64*fs, 35us @ 44.1 Khz) */
L3HaltTime = 1, /* 190 ns */
};
/*
* Grab control of the IIC/L3 shared pins
*/
static void
L3acquirepins(void)
{
GpioReg *g = GPIOREG;
int s;
s = splhi();
g->gpsr = (L3Mode | L3Clock | L3Data);
g->gpdr |= (L3Mode | L3Clock | L3Data);
splx(s);
// microdelay(2);
}
/*
* Release control of the IIC/L3 shared pins
*/
static void
L3releasepins(void)
{
GpioReg *g = GPIOREG;
int s;
s = splhi();
g->gpdr &= ~(L3Mode | L3Clock | L3Data);
splx(s);
}
/*
* Initialize the interface
*/
void
L3init(void)
{
GpioReg *g = GPIOREG;
int s;
s = splhi();
g->gafr &= ~(L3Data | L3Clock | L3Mode);
splx(s);
L3releasepins();
}
/*
* Send a byte. The mode line is set or pulsed based on the mode sequence
* count. The mode line is high on entry and exit. The mod line is pulsed
* before the second data byte and before ech byte thereafter.
*/
static void
L3sendbyte(int data, int mode)
{
int i;
GpioReg *g = GPIOREG;
switch(mode) {
case 0: /* Address mode */
g->gpcr = L3Mode;
break;
case 1: /* First data byte */
break;
default: /* Subsequent bytes */
g->gpcr = L3Mode;
microdelay(L3HaltTime);
g->gpsr = L3Mode;
break;
}
microdelay(L3ModeSetupTime);
for (i = 0; i < 8; i++){
microdelay(2);
/*
* Send a bit. The clock is high on entry and on exit. Data is sent only
* when the clock is low (I2C compatibility).
*/
g->gpcr = L3Clock;
if (data & (1<<i))
g->gpsr = L3Data;
else
g->gpcr = L3Data;
/* Assumes L3DataSetupTime < L3ClockLowTime */
microdelay(L3ClockLowTime);
g->gpsr = L3Clock;
microdelay(L3ClockHighTime);
}
if (mode == 0) /* Address mode */
g->gpsr = L3Mode;
microdelay(L3ModeHoldTime);
}
/*
* Get a byte. The mode line is set or pulsed based on the mode sequence
* count. The mode line is high on entry and exit. The mod line is pulsed
* before the second data byte and before each byte thereafter. This
* function is never valid with mode == 0 (address cycle) as the address
* is always sent on the bus, not read.
*/
static int
L3getbyte(int mode)
{
int data = 0;
int i;
GpioReg *g = GPIOREG;
switch(mode) {
case 0: /* Address mode - never valid */
break;
case 1: /* First data byte */
break;
default: /* Subsequent bytes */
g->gpcr = L3Mode;
microdelay(L3HaltTime);
g->gpsr = L3Mode;
break;
}
microdelay(L3ModeSetupTime);
for (i = 0; i < 8; i++){
/*
* Get a bit. The clock is high on entry and on exit. Data is read after
* the clock low time has expired.
*/
g->gpcr = L3Clock;
microdelay(L3ClockLowTime);
if(g->gplr & L3Data)
data |= 1<<i;
g->gpsr = L3Clock;
microdelay(L3ClockHighTime);
}
microdelay(L3ModeHoldTime);
return data;
}
/*
* Write data to a device on the L3 bus. The address is passed as well as
* the data and length. The length written is returned. The register space
* is encoded in the address (low two bits are set and device address is
* in the upper 6 bits).
*/
int
L3write(int addr, void *data, int len)
{
int mode = 0;
int bytes = len;
uchar *b;
L3acquirepins();
L3sendbyte(addr, mode++);
for(b = data; --len >= 0;)
L3sendbyte(*b++, mode++);
L3releasepins();
return bytes;
}
/*
* Read data from a device on the L3 bus. The address is passed as well as
* the data and length. The length read is returned. The register space
* is encoded in the address (low two bits are set and device address is
* in the upper 6 bits).
*/
int
L3read(int addr, void *data, int len)
{
int mode = 0;
int bytes = len;
uchar *b;
int s;
L3acquirepins();
L3sendbyte(addr, mode++);
s = splhi();
GPIOREG->gpdr &= ~(L3Data);
splx(s);
for(b = data; --len >= 0;)
*b++ = L3getbyte(mode++);
L3releasepins();
return bytes;
}
|