diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2005-11-14 19:50:16 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2005-11-14 19:50:16 +0000 |
commit | 3b0e8f89ab443c8d07b94165e6a301066b8e7ea1 (patch) | |
tree | 3ff4ffd2a0af1f8ea04852b930fe805dec48e4e0 | |
parent | 362259ad3e1d969d2b515248ea6bb050808a222c (diff) | |
download | rockbox-3b0e8f89ab443c8d07b94165e6a301066b8e7ea1.tar.gz rockbox-3b0e8f89ab443c8d07b94165e6a301066b8e7ea1.zip |
H300: First lame attempt to a pcf50606 driver
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7873 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | firmware/drivers/pcf50606.c | 298 | ||||
-rw-r--r-- | firmware/export/pcf50606.h | 28 |
2 files changed, 326 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 | |||
48 | static 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 | |||
60 | static void pcf50606_i2c_stop(void) | ||
61 | { | ||
62 | SDA_LO; | ||
63 | SCL_HI; | ||
64 | DELAY; | ||
65 | SDA_HI; | ||
66 | } | ||
67 | |||
68 | |||
69 | static 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 | |||
99 | static 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 | |||
133 | static 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 | |||
156 | static 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 | |||
178 | int 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 | |||
204 | int 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 | |||
235 | int 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 */ | ||
267 | static 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 | |||
281 | void 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 | } | ||
diff --git a/firmware/export/pcf50606.h b/firmware/export/pcf50606.h new file mode 100644 index 0000000000..3941ea68d4 --- /dev/null +++ b/firmware/export/pcf50606.h | |||
@@ -0,0 +1,28 @@ | |||
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 | #ifndef PCF50606_H | ||
20 | #define PCF50606_H | ||
21 | |||
22 | #ifdef IRIVER_H300_SERIES | ||
23 | void pcf50606_init(void); | ||
24 | int pcf50606_write(int address, const unsigned char* buf, int count); | ||
25 | int pcf50606_read(int address, unsigned char* buf, int count); | ||
26 | #endif | ||
27 | |||
28 | #endif | ||