summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Arnold <amiconn@rockbox.org>2006-04-16 23:16:32 +0000
committerJens Arnold <amiconn@rockbox.org>2006-04-16 23:16:32 +0000
commitf2897e81807bf66b5a391d82bca163d59008ccb0 (patch)
treef085f4e14e7f5a1a642dad52bfe43fc951faf672
parent295c226ed507603a485e3524723f20741b9bdd7e (diff)
downloadrockbox-f2897e81807bf66b5a391d82bca163d59008ccb0.tar.gz
rockbox-f2897e81807bf66b5a391d82bca163d59008ccb0.zip
H300: Optimised PCF50606 driver, significantly reduces CPU power drain from the button tick (with both main & remote buttons: 50%->13% at 11MHz, 12%->6% at 45MHz): * Delay is adapted to the current CPU clock, aiming at constant 400kHz i2c clock. * Reduced number of port accesses (accessing GPIO is very slow, especially with the atomic boolean instructions) by implementing an open-collector-like behaviour. * Time-critical functions implemented in assembler.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@9693 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/drivers/pcf50606.c418
-rw-r--r--firmware/export/pcf50606.h1
-rw-r--r--firmware/system.c12
3 files changed, 346 insertions, 85 deletions
diff --git a/firmware/drivers/pcf50606.c b/firmware/drivers/pcf50606.c
index 51cd812346..5c8f8d0e96 100644
--- a/firmware/drivers/pcf50606.c
+++ b/firmware/drivers/pcf50606.c
@@ -27,123 +27,373 @@
27 27
28/* TODO: merge all bit-banged I2C into a generic I2C driver */ 28/* TODO: merge all bit-banged I2C into a generic I2C driver */
29 29
30#define USE_ASM
30 31
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) 32#define SDA ( 0x00002000 & GPIO1_READ)
33#define SDA_LO_OUT or_l( 0x00002000, &GPIO1_ENABLE)
34#define SDA_HI_IN and_l(~0x00002000, &GPIO1_ENABLE)
36 35
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 SCL_INPUT;while(!SCL){};or_l(0x1000, &GPIO_OUT);SCL_OUTPUT
42#define SCL ( 0x00001000 & GPIO_READ) 36#define SCL ( 0x00001000 & GPIO_READ)
37#define SCL_LO_OUT or_l( 0x00001000, &GPIO_ENABLE)
38#define SCL_HI_IN and_l(~0x00001000, &GPIO_ENABLE); while(!SCL);
43 39
44/* delay loop to achieve 400kHz at 120MHz CPU frequency */ 40#define DELAY \
45#define DELAY do { int _x; for(_x=0;_x<22;_x++);} while(0) 41 asm ( \
42 "move.l %[dly],%%d0 \n" \
43 "1: \n" \
44 "subq.l #1,%%d0 \n" \
45 "bhi.s 1b \n" \
46 : : [dly]"d"(i2c_delay) : "d0" );
46 47
47static void pcf50606_i2c_start(void) 48static int i2c_delay IDATA_ATTR = 44;
49
50void pcf50606_i2c_recalc_delay(int cpu_clock)
48{ 51{
49 SDA_OUTPUT; 52 i2c_delay = MAX(cpu_clock / (400000*2*3) - 7, 1);
50 SCL_OUTPUT; 53}
51 SDA_HI; 54
52 SCL_HI; 55static inline void pcf50606_i2c_start(void)
56{
57#ifdef USE_ASM
58 asm (
59 "not.l %[sdab] \n" /* SDA_HI_IN */
60 "and.l %[sdab],(8,%[gpi1]) \n"
61 "not.l %[sdab] \n"
62
63 "not.l %[sclb] \n" /* SCL_HI_IN */
64 "and.l %[sclb],(8,%[gpio]) \n"
65 "not.l %[sclb] \n"
66 "1: \n"
67 "move.l (%[gpio]),%%d0 \n"
68 "btst.l #12,%%d0 \n"
69 "beq.s 1b \n"
70
71 "move.l %[dly],%%d0 \n" /* DELAY */
72 "1: \n"
73 "subq.l #1,%%d0 \n"
74 "bhi.s 1b \n"
75
76 "or.l %[sdab],(8,%[gpi1]) \n" /* SDA_LO_OUT */
77
78 "move.l %[dly],%%d0 \n" /* DELAY */
79 "1: \n"
80 "subq.l #1,%%d0 \n"
81 "bhi.s 1b \n"
82
83 "or.l %[sclb],(8,%[gpio]) \n" /* SCL_LO_OUT */
84 : /* outputs */
85 : /* inputs */
86 [gpio]"a"(&GPIO_READ),
87 [sclb]"d"(0x00001000),
88 [gpi1]"a"(&GPIO1_READ),
89 [sdab]"d"(0x00002000),
90 [dly] "d"(i2c_delay)
91 : /* clobbers */
92 "d0"
93 );
94#else
95 SDA_HI_IN;
96 SCL_HI_IN;
53 DELAY; 97 DELAY;
54 SDA_LO; 98 SDA_LO_OUT;
55 DELAY; 99 DELAY;
56 SCL_LO; 100 SCL_LO_OUT;
101#endif
57} 102}
58 103
59static void pcf50606_i2c_stop(void) 104static inline void pcf50606_i2c_stop(void)
60{ 105{
61 SDA_LO; 106#ifdef USE_ASM
62 SCL_HI; 107 asm (
63 DELAY; 108 "or.l %[sdab],(8,%[gpi1]) \n" /* SDA_LO_OUT */
64 SDA_HI; 109
110 "not.l %[sclb] \n" /* SCL_HI_IN */
111 "and.l %[sclb],(8,%[gpio]) \n"
112 "not.l %[sclb] \n"
113 "1: \n"
114 "move.l (%[gpio]),%%d0 \n"
115 "btst.l #12,%%d0 \n"
116 "beq.s 1b \n"
117
118 "move.l %[dly],%%d0 \n" /* DELAY */
119 "1: \n"
120 "subq.l #1,%%d0 \n"
121 "bhi.s 1b \n"
122
123 "not.l %[sdab] \n" /* SDA_HI_IN */
124 "and.l %[sdab],(8,%[gpi1]) \n"
125 "not.l %[sdab] \n"
126 : /* outputs */
127 : /* inputs */
128 [gpio]"a"(&GPIO_READ),
129 [sclb]"d"(0x00001000),
130 [gpi1]"a"(&GPIO1_READ),
131 [sdab]"d"(0x00002000),
132 [dly] "d"(i2c_delay)
133 : /* clobbers */
134 "d0"
135 );
136#else
137 SDA_LO_OUT;
138 SCL_HI_IN;
139 DELAY;
140 SDA_HI_IN;
141#endif
65} 142}
66 143
67 144static inline void pcf50606_i2c_ack(bool ack)
68static void pcf50606_i2c_ack(bool ack)
69{ 145{
70 SCL_LO; /* Set the clock low */ 146#ifdef USE_ASM
147 asm (
148 "tst.b %[ack] \n" /* if (!ack) */
149 "bne.s 1f \n"
150
151 "not.l %[sdab] \n" /* SDA_HI_IN */
152 "and.l %[sdab],(8,%[gpi1]) \n"
153 "not.l %[sdab] \n"
154 ".word 0x51fb \n" /* trapf.l : else */
155 "1: \n"
156 "or.l %[sdab],(8,%[gpi1]) \n" /* SDA_LO_OUT */
157
158 "not.l %[sclb] \n" /* SCL_HI_IN */
159 "and.l %[sclb],(8,%[gpio]) \n"
160 "not.l %[sclb] \n"
161 "1: \n"
162 "move.l (%[gpio]),%%d0 \n"
163 "btst.l #12,%%d0 \n"
164 "beq.s 1b \n"
165
166 "move.l %[dly],%%d0 \n" /* DELAY */
167 "1: \n"
168 "subq.l #1,%%d0 \n"
169 "bhi.s 1b \n"
170
171 "or.l %[sclb],(8,%[gpio]) \n" /* SCL_LO_OUT */
172 : /* outputs */
173 : /* inputs */
174 [gpio]"a"(&GPIO_READ),
175 [sclb]"d"(0x00001000),
176 [gpi1]"a"(&GPIO1_READ),
177 [sdab]"d"(0x00002000),
178 [dly] "d"(i2c_delay),
179 [ack] "d"(ack)
180 : /* clobbers */
181 "d0"
182 );
183#else
71 if(ack) 184 if(ack)
72 SDA_LO; 185 SDA_LO_OUT;
73 else 186 else
74 SDA_HI; 187 SDA_HI_IN;
75 188
76 SCL_HI; 189 SCL_HI_IN;
77 190
78 DELAY; 191 DELAY;
79 SCL_OUTPUT; 192 SCL_LO_OUT;
80 SCL_LO; 193#endif
81} 194}
82 195
83static int pcf50606_i2c_getack(void) 196static inline bool pcf50606_i2c_getack(void)
84{ 197{
85 int ret = 1; 198 bool ret;
86 199
87 SDA_INPUT; /* And set to input */ 200#ifdef USE_ASM
201 asm (
202 "not.l %[sdab] \n" /* SDA_HI_IN */
203 "and.l %[sdab],(8,%[gpi1]) \n"
204 "not.l %[sdab] \n"
205
206 "move.l %[dly],%%d0 \n" /* DELAY */
207 "1: \n"
208 "subq.l #1,%%d0 \n"
209 "bhi.s 1b \n"
210
211 "not.l %[sclb] \n" /* SCL_HI_IN */
212 "and.l %[sclb],(8,%[gpio]) \n"
213 "not.l %[sclb] \n"
214 "1: \n"
215 "move.l (%[gpio]),%%d0 \n"
216 "btst.l #12,%%d0 \n"
217 "beq.s 1b \n"
218
219 "move.l (%[gpi1]),%%d0 \n" /* ret = !SDA */
220 "btst.l #13,%%d0 \n"
221 "seq.b %[ret] \n"
222
223 "or.l %[sclb],(8,%[gpio]) \n" /* SCL_LO_OUT */
224
225 "move.l %[dly],%%d0 \n" /* DELAY */
226 "1: \n"
227 "subq.l #1,%%d0 \n"
228 "bhi.s 1b \n"
229 : /* outputs */
230 [ret]"=&r"(ret)
231 : /* inputs */
232 [gpio]"a"(&GPIO_READ),
233 [sclb]"d"(0x00001000),
234 [gpi1]"a"(&GPIO1_READ),
235 [sdab]"d"(0x00002000),
236 [dly] "d"(i2c_delay)
237 : /* clobbers */
238 "d0"
239 );
240#else
241 SDA_HI_IN;
88 DELAY; 242 DELAY;
89 SCL_HI; 243 SCL_HI_IN;
90 244
91 if (SDA) 245 ret = !SDA;
92 /* ack failed */ 246
93 ret = 0; 247 SCL_LO_OUT;
94
95 SCL_OUTPUT;
96 SCL_LO;
97 SDA_HI;
98 SDA_OUTPUT;
99 DELAY; 248 DELAY;
249#endif
100 250
101 return ret; 251 return ret;
102} 252}
103 253
104static void pcf50606_i2c_outb(unsigned char byte) 254static void pcf50606_i2c_outb(unsigned char byte)
105{ 255{
106 int i; 256#ifdef USE_ASM
107 257 asm volatile (
108 /* clock out each bit, MSB first */ 258 "moveq.l #24,%%d0 \n" /* byte <<= 24 */
109 for ( i=0x80; i; i>>=1 ) { 259 "lsl.l %%d0,%[byte] \n"
110 if ( i & byte ) 260 "moveq.l #8,%%d1 \n" /* i = 8 */
111 { 261
112 SDA_HI; 262 "2: \n" /* do */
113 } 263 "lsl.l #1,%[byte] \n" /* if ((byte <<= 1) carry) */
114 else 264 "bcc.s 1f \n"
115 { 265
116 SDA_LO; 266 "not.l %[sdab] \n" /* SDA_HI_IN */
117 } 267 "and.l %[sdab],(8,%[gpi1]) \n"
118 DELAY; 268 "not.l %[sdab] \n"
119 SCL_HI; 269 ".word 0x51fb \n" /* trapf.l; else */
120 DELAY; 270 "1: \n"
121 SCL_LO; 271 "or.l %[sdab],(8,%[gpi1]) \n" /* SDA_LO_OUT */
122 } 272
123 273 "move.l %[dly],%%d0 \n" /* DELAY */
124 SDA_HI; 274 "1: \n"
275 "subq.l #1,%%d0 \n"
276 "bhi.s 1b \n"
277
278 "not.l %[sclb] \n" /* SCL_HI_IN */
279 "and.l %[sclb],(8,%[gpio]) \n"
280 "not.l %[sclb] \n"
281 "1: \n"
282 "move.l (%[gpio]),%%d0 \n"
283 "btst.l #12,%%d0 \n"
284 "beq.s 1b \n"
285
286 "move.l %[dly],%%d0 \n" /* DELAY */
287 "1: \n"
288 "subq.l #1,%%d0 \n"
289 "bhi.s 1b \n"
290
291 "or.l %[sclb],(8,%[gpio]) \n" /* SCL_LO_OUT */
292
293 "subq.l #1,%%d1 \n" /* i-- */
294 "bne.s 2b \n" /* while (i != 0) */
295 : /* outputs */
296 [byte]"+d"(byte)
297 : /* inputs */
298 [gpio]"a"(&GPIO_READ),
299 [sclb]"d"(0x00001000),
300 [gpi1]"a"(&GPIO1_READ),
301 [sdab]"d"(0x00002000),
302 [dly] "d"(i2c_delay)
303 : /* clobbers */
304 "d0", "d1"
305 );
306#else
307 int i;
308
309 /* clock out each bit, MSB first */
310 for ( i=0x80; i; i>>=1 )
311 {
312 if ( i & byte )
313 SDA_HI_IN;
314 else
315 SDA_LO_OUT;
316 DELAY;
317 SCL_HI_IN;
318 DELAY;
319 SCL_LO_OUT;
320 }
321#endif
125} 322}
126 323
127static unsigned char pcf50606_i2c_inb(bool ack) 324static unsigned char pcf50606_i2c_inb(bool ack)
128{ 325{
129 int i; 326 unsigned char byte = 0;
130 unsigned char byte = 0; 327
131 328#ifdef USE_ASM
132 /* clock in each bit, MSB first */ 329 asm (
133 for ( i=0x80; i; i>>=1 ) { 330 "not.l %[sdab] \n" /* SDA_HI_IN */
134 SDA_INPUT; /* And set to input */ 331 "and.l %[sdab],(8,%[gpi1]) \n"
135 SCL_HI; 332 "not.l %[sdab] \n"
136 DELAY; 333
137 if ( SDA ) 334 "moveq.l #8,%%d1 \n" /* i = 8 */
138 byte |= i; 335 "clr.l %[byte] \n" /* byte = 0 */
139 SCL_LO; 336
140 DELAY; 337 "2: \n" /* do */
141 SDA_OUTPUT; 338 "not.l %[sclb] \n" /* SCL_HI_IN */
142 } 339 "and.l %[sclb],(8,%[gpio]) \n"
143 340 "not.l %[sclb] \n"
144 pcf50606_i2c_ack(ack); 341 "1: \n"
342 "move.l (%[gpio]),%%d0 \n"
343 "btst.l #12,%%d0 \n"
344 "beq.s 1b \n"
345
346 "move.l %[dly],%%d0 \n" /* DELAY */
347 "1: \n"
348 "subq.l #1,%%d0 \n"
349 "bhi.s 1b \n"
350
351 "lsl.l #1,%[byte] \n" /* byte <<= 1 */
352 "move.l (%[gpi1]),%%d0 \n" /* if (SDA) */
353 "btst.l #13,%%d0 \n"
354 "beq.s 1f \n"
355 "addq.l #1,%[byte] \n" /* byte++ */
356 "1: \n"
357
358 "or.l %[sclb],(8,%[gpio]) \n" /* SCL_LO_OUT */
359
360 "move.l %[dly],%%d0 \n" /* DELAY */
361 "1: \n"
362 "subq.l #1,%%d0 \n"
363 "bhi.s 1b \n"
364
365 "subq.l #1,%%d1 \n" /* i-- */
366 "bne.s 2b \n" /* while (i != 0) */
367 : /* outputs */
368 [byte]"=&d"(byte)
369 : /* inputs */
370 [gpio]"a"(&GPIO_READ),
371 [sclb]"d"(0x00001000),
372 [gpi1]"a"(&GPIO1_READ),
373 [sdab]"d"(0x00002000),
374 [dly] "d"(i2c_delay)
375 : /* clobbers */
376 "d0", "d1"
377 );
378#else
379 int i;
380
381 /* clock in each bit, MSB first */
382 SDA_HI_IN;
383 for ( i=0x80; i; i>>=1 )
384 {
385 SCL_HI_IN;
386 DELAY;
387 if ( SDA )
388 byte |= i;
389 SCL_LO_OUT;
390 DELAY;
391 }
392#endif
393
394 pcf50606_i2c_ack(ack);
145 395
146 return byte; 396 return byte;
147} 397}
148 398
149int pcf50606_i2c_write(int address, const unsigned char* buf, int count) 399int pcf50606_i2c_write(int address, const unsigned char* buf, int count)
@@ -270,12 +520,12 @@ static void set_voltages(void)
270void pcf50606_init(void) 520void pcf50606_init(void)
271{ 521{
272 /* Bit banged I2C */ 522 /* Bit banged I2C */
273 or_l(0x00002000, &GPIO1_OUT);
274 or_l(0x00001000, &GPIO_OUT);
275 or_l(0x00002000, &GPIO1_ENABLE);
276 or_l(0x00001000, &GPIO_ENABLE);
277 or_l(0x00002000, &GPIO1_FUNCTION); 523 or_l(0x00002000, &GPIO1_FUNCTION);
278 or_l(0x00001000, &GPIO_FUNCTION); 524 or_l(0x00001000, &GPIO_FUNCTION);
525 and_l(~0x00002000, &GPIO1_OUT);
526 and_l(~0x00001000, &GPIO_OUT);
527 and_l(~0x00002000, &GPIO1_ENABLE);
528 and_l(~0x00001000, &GPIO_ENABLE);
279 529
280 set_voltages(); 530 set_voltages();
281 531
diff --git a/firmware/export/pcf50606.h b/firmware/export/pcf50606.h
index cbfaeb1135..d9f44bb577 100644
--- a/firmware/export/pcf50606.h
+++ b/firmware/export/pcf50606.h
@@ -20,6 +20,7 @@
20#define PCF50606_H 20#define PCF50606_H
21 21
22void pcf50606_init(void); 22void pcf50606_init(void);
23void pcf50606_i2c_recalc_delay(int cpu_clock);
23int pcf50606_write_multiple(int address, const unsigned char* buf, int count); 24int pcf50606_write_multiple(int address, const unsigned char* buf, int count);
24int pcf50606_write(int address, unsigned char val); 25int pcf50606_write(int address, unsigned char val);
25int pcf50606_read_multiple(int address, unsigned char* buf, int count); 26int pcf50606_read_multiple(int address, unsigned char* buf, int count);
diff --git a/firmware/system.c b/firmware/system.c
index 8bdd821e60..b41337b8fe 100644
--- a/firmware/system.c
+++ b/firmware/system.c
@@ -594,6 +594,13 @@ int system_memory_guard(int newmode)
594#define DEFAULT_REFRESH_TIMER 1 594#define DEFAULT_REFRESH_TIMER 1
595#endif 595#endif
596 596
597#ifdef IRIVER_H300_SERIES
598#define RECALC_DELAYS(f) \
599 pcf50606_i2c_recalc_delay(f)
600#else
601#define RECALC_DELAYS(f)
602#endif
603
597void set_cpu_frequency (long) __attribute__ ((section (".icode"))); 604void set_cpu_frequency (long) __attribute__ ((section (".icode")));
598void set_cpu_frequency(long frequency) 605void set_cpu_frequency(long frequency)
599{ 606{
@@ -604,6 +611,7 @@ void set_cpu_frequency(long frequency)
604 /* Refresh timer for bypass frequency */ 611 /* Refresh timer for bypass frequency */
605 PLLCR &= ~1; /* Bypass mode */ 612 PLLCR &= ~1; /* Bypass mode */
606 timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false); 613 timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false);
614 RECALC_DELAYS(CPUFREQ_MAX);
607 PLLCR = 0x11c56005; 615 PLLCR = 0x11c56005;
608 CSCR0 = 0x00001180; /* Flash: 4 wait states */ 616 CSCR0 = 0x00001180; /* Flash: 4 wait states */
609 CSCR1 = 0x00000980; /* LCD: 2 wait states */ 617 CSCR1 = 0x00000980; /* LCD: 2 wait states */
@@ -615,12 +623,13 @@ void set_cpu_frequency(long frequency)
615 IDECONFIG1 = 0x106000 | (5 << 10); /* BUFEN2 enable + CS2Pre/CS2Post */ 623 IDECONFIG1 = 0x106000 | (5 << 10); /* BUFEN2 enable + CS2Pre/CS2Post */
616 IDECONFIG2 = 0x40000 | (1 << 8); /* TA enable + CS2wait */ 624 IDECONFIG2 = 0x40000 | (1 << 8); /* TA enable + CS2wait */
617 break; 625 break;
618 626
619 case CPUFREQ_NORMAL: 627 case CPUFREQ_NORMAL:
620 DCR = (DCR & ~0x01ff) | DEFAULT_REFRESH_TIMER; 628 DCR = (DCR & ~0x01ff) | DEFAULT_REFRESH_TIMER;
621 /* Refresh timer for bypass frequency */ 629 /* Refresh timer for bypass frequency */
622 PLLCR &= ~1; /* Bypass mode */ 630 PLLCR &= ~1; /* Bypass mode */
623 timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false); 631 timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, false);
632 RECALC_DELAYS(CPUFREQ_NORMAL);
624 PLLCR = 0x13c5e005; 633 PLLCR = 0x13c5e005;
625 CSCR0 = 0x00000580; /* Flash: 1 wait state */ 634 CSCR0 = 0x00000580; /* Flash: 1 wait state */
626 CSCR1 = 0x00000180; /* LCD: 0 wait states */ 635 CSCR1 = 0x00000180; /* LCD: 0 wait states */
@@ -637,6 +646,7 @@ void set_cpu_frequency(long frequency)
637 /* Refresh timer for bypass frequency */ 646 /* Refresh timer for bypass frequency */
638 PLLCR &= ~1; /* Bypass mode */ 647 PLLCR &= ~1; /* Bypass mode */
639 timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, true); 648 timers_adjust_prescale(CPUFREQ_DEFAULT_MULT, true);
649 RECALC_DELAYS(CPUFREQ_DEFAULT);
640 PLLCR = 0x10c00200; /* Power down PLL, but keep CLSEL and CRSEL */ 650 PLLCR = 0x10c00200; /* Power down PLL, but keep CLSEL and CRSEL */
641 CSCR0 = 0x00000180; /* Flash: 0 wait states */ 651 CSCR0 = 0x00000180; /* Flash: 0 wait states */
642 CSCR1 = 0x00000180; /* LCD: 0 wait states */ 652 CSCR1 = 0x00000180; /* LCD: 0 wait states */