summaryrefslogtreecommitdiff
path: root/firmware/drivers/pcf50606.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/pcf50606.c')
-rw-r--r--firmware/drivers/pcf50606.c298
1 files changed, 298 insertions, 0 deletions
diff --git a/firmware/drivers/pcf50606.c b/firmware/drivers/pcf50606.c
new file mode 100644
index 0000000000..993e56800d
--- /dev/null
+++ b/firmware/drivers/pcf50606.c
@@ -0,0 +1,298 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#include "config.h"
20#include "cpu.h"
21#include <stdbool.h>
22#include "kernel.h"
23#include "system.h"
24#include "hwcompat.h"
25#include "logf.h"
26#include "string.h"
27
28/* TODO: merge all bit-banged I2C into a generic I2C driver */
29
30
31#define SDA_LO and_l(~0x00002000, &GPIO1_OUT)
32#define SDA_HI or_l( 0x00002000, &GPIO1_OUT)
33#define SDA_INPUT and_l(~0x00002000, &GPIO1_ENABLE)
34#define SDA_OUTPUT or_l( 0x00002000, &GPIO1_ENABLE)
35#define SDA ( 0x00002000 & GPIO1_READ)
36
37/* SCL is GPIO, 3 */
38#define SCL_INPUT and_l(~0x00001000, &GPIO_ENABLE)
39#define SCL_OUTPUT or_l( 0x00001000, &GPIO_ENABLE)
40#define SCL_LO and_l(~0x00001000, &GPIO_OUT)
41#define SCL_HI or_l( 0x00001000, &GPIO_OUT)
42#define SCL ( 0x00001000 & GPIO_READ)
43
44/* delay loop to achieve 400kHz at 120MHz CPU frequency */
45#define DELAY do { int _x; for(_x=0;_x<22;_x++);} while(0)
46
47
48static void pcf50606_i2c_start(void)
49{
50 SDA_OUTPUT;
51 SCL_OUTPUT;
52 SDA_HI;
53 SCL_HI;
54 DELAY;
55 SDA_LO;
56 DELAY;
57 SCL_LO;
58}
59
60static void pcf50606_i2c_stop(void)
61{
62 SDA_LO;
63 SCL_HI;
64 DELAY;
65 SDA_HI;
66}
67
68
69static void pcf50606_i2c_ack(bool ack)
70{
71 /* Here's the deal. The slave is slow, and sometimes needs to wait
72 before it can receive the acknowledge. Therefore it forces the clock
73 low until it is ready. We need to poll the clock line until it goes
74 high before we release the ack.
75
76 In their infinite wisdom, iriver didn't pull up the SCL line, so
77 we have to drive the SCL high repeatedly to simulate a pullup. */
78
79 SCL_LO; /* Set the clock low */
80 if(ack)
81 SDA_LO;
82 else
83 SDA_HI;
84
85 SCL_INPUT; /* Set the clock to input */
86 while(!SCL) /* and wait for the slave to release it */
87 {
88 SCL_OUTPUT; /* Set the clock to output */
89 SCL_HI;
90 SCL_INPUT; /* Set the clock to input */
91 DELAY;
92 }
93
94 DELAY;
95 SCL_OUTPUT;
96 SCL_LO;
97}
98
99static int pcf50606_i2c_getack(void)
100{
101 int ret = 1;
102
103 /* Here's the deal. The slave is slow, and sometimes needs to wait
104 before it can send the acknowledge. Therefore it forces the clock
105 low until it is ready. We need to poll the clock line until it goes
106 high before we read the ack.
107
108 In their infinite wisdom, iriver didn't pull up the SCL line, so
109 we have to drive the SCL high repeatedly to simulate a pullup. */
110
111 SDA_INPUT; /* And set to input */
112 SCL_INPUT; /* Set the clock to input */
113 while(!SCL) /* and wait for the slave to release it */
114 {
115 SCL_OUTPUT; /* Set the clock to output */
116 SCL_HI;
117 SCL_INPUT; /* Set the clock to input */
118 DELAY;
119 }
120
121 if (SDA)
122 /* ack failed */
123 ret = 0;
124
125 SCL_OUTPUT;
126 SCL_LO;
127 SDA_HI;
128 SDA_OUTPUT;
129
130 return ret;
131}
132
133static void pcf50606_i2c_outb(unsigned char byte)
134{
135 int i;
136
137 /* clock out each bit, MSB first */
138 for ( i=0x80; i; i>>=1 ) {
139 if ( i & byte )
140 {
141 SDA_HI;
142 }
143 else
144 {
145 SDA_LO;
146 }
147 DELAY;
148 SCL_HI;
149 DELAY;
150 SCL_LO;
151 }
152
153 SDA_HI;
154}
155
156static unsigned char pcf50606_i2c_inb(bool ack)
157{
158 int i;
159 unsigned char byte = 0;
160
161 /* clock in each bit, MSB first */
162 for ( i=0x80; i; i>>=1 ) {
163 SDA_INPUT; /* And set to input */
164 SCL_HI;
165 DELAY;
166 if ( SDA )
167 byte |= i;
168 SCL_LO;
169 DELAY;
170 SDA_OUTPUT;
171 }
172
173 pcf50606_i2c_ack(ack);
174
175 return byte;
176}
177
178int pcf50606_i2c_write(int address, const unsigned char* buf, int count)
179{
180 int i,x=0;
181
182 pcf50606_i2c_start();
183 pcf50606_i2c_outb(address & 0xfe);
184 if (pcf50606_i2c_getack())
185 {
186 for (i=0; i<count; i++)
187 {
188 pcf50606_i2c_outb(buf[i]);
189 if (!pcf50606_i2c_getack())
190 {
191 x=-2;
192 break;
193 }
194 }
195 }
196 else
197 {
198 logf("pcf50606_i2c_write() - no ack\n");
199 x=-1;
200 }
201 return x;
202}
203
204int pcf50606_read(int address, unsigned char* buf, int count)
205{
206 int i=0;
207 int ret = 0;
208 unsigned char obuf[1];
209
210 obuf[0] = address;
211
212 /* send read command */
213 if (pcf50606_i2c_write(0x10, obuf, 1) >= 0)
214 {
215 pcf50606_i2c_start();
216 pcf50606_i2c_outb(0x11);
217 if (pcf50606_i2c_getack())
218 {
219 for(i = 0;i < count-1;i++)
220 buf[i] = pcf50606_i2c_inb(true);
221
222 buf[i] = pcf50606_i2c_inb(false);
223 }
224 else
225 {
226 ret = -1;
227 }
228 }
229
230 pcf50606_i2c_stop();
231
232 return ret;
233}
234
235int pcf50606_write(int address, const unsigned char* buf, int count)
236{
237 unsigned char obuf[1];
238 int i;
239 int ret = 0;
240
241 obuf[0] = address;
242
243 /* send write command */
244 if (pcf50606_i2c_write(0x10, obuf, 1) >= 0)
245 {
246 for (i=0; i<count; i++)
247 {
248 pcf50606_i2c_outb(buf[i]);
249 if (!pcf50606_i2c_getack())
250 {
251 ret = -2;
252 break;
253 }
254 }
255 }
256 else
257 {
258 ret = -1;
259 }
260
261 pcf50606_i2c_stop();
262 return ret;
263}
264
265/* These voltages were determined by measuring the output of the PCF50606
266 on a running H300, and verified by disassembling the original firmware */
267static void set_voltages(void)
268{
269 static const unsigned char buf[5] =
270 {
271 0xf4, /* IOREGC = 3.3V, ON in all states */
272 0xef, /* D1REGC = 2.4V, ON in all states */
273 0x18, /* D2REGC = 3.3V, OFF in all states */
274 0xf0, /* D3REGC = 2.5V, ON in all states */
275 0xef, /* LPREGC1 = 2.4V, ON in all states */
276 };
277
278 pcf50606_write(0x23, buf, 5);
279}
280
281void pcf50606_init(void)
282{
283 unsigned char c;
284
285 /* Bit banged I2C */
286 or_l(0x00002000, &GPIO1_OUT);
287 or_l(0x00001000, &GPIO_OUT);
288 or_l(0x00002000, &GPIO1_ENABLE);
289 or_l(0x00001000, &GPIO_ENABLE);
290 or_l(0x00002000, &GPIO1_FUNCTION);
291 or_l(0x00001000, &GPIO_FUNCTION);
292
293 set_voltages();
294
295 /* Backlight PWM = 512Hz 50/50 */
296 c = 0x13;
297 pcf50606_write(0x35, &c, 1);
298}