diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2013-05-11 19:59:01 -0400 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2013-05-11 21:09:01 -0400 |
commit | 0a7d941fb9e8dba5934255e37ceaa9063b38ed25 (patch) | |
tree | 5e972b74abfd73c662430672cd57997c4a8056d0 /firmware/target/arm/imx31 | |
parent | 4877f618d664f53694132b91fc7712844566bfbb (diff) | |
download | rockbox-0a7d941fb9e8dba5934255e37ceaa9063b38ed25.tar.gz rockbox-0a7d941fb9e8dba5934255e37ceaa9063b38ed25.zip |
i.MX31: Remove long udelay from DVFS interrupt handler
Split the ISR into two parts and alllow quick return from first half.
Introduces a uevent() API to have a callback happen in a specified
number of microseconds. Right now only one event is supported.
Change-Id: Ib1666165be2f6082e5275d64961f083cab104f9f
Diffstat (limited to 'firmware/target/arm/imx31')
6 files changed, 140 insertions, 88 deletions
diff --git a/firmware/target/arm/imx31/avic-imx31.h b/firmware/target/arm/imx31/avic-imx31.h index 7398c8464c..50b04635ee 100644 --- a/firmware/target/arm/imx31/avic-imx31.h +++ b/firmware/target/arm/imx31/avic-imx31.h | |||
@@ -25,6 +25,7 @@ | |||
25 | #define INT_PRIO_DEFAULT 7 | 25 | #define INT_PRIO_DEFAULT 7 |
26 | #define INT_PRIO_DVFS (INT_PRIO_DEFAULT+1) | 26 | #define INT_PRIO_DVFS (INT_PRIO_DEFAULT+1) |
27 | #define INT_PRIO_DPTC (INT_PRIO_DEFAULT+1) | 27 | #define INT_PRIO_DPTC (INT_PRIO_DEFAULT+1) |
28 | #define INT_PRIO_GPT (INT_PRIO_DEFAULT+1) | ||
28 | #define INT_PRIO_SDMA (INT_PRIO_DEFAULT+2) | 29 | #define INT_PRIO_SDMA (INT_PRIO_DEFAULT+2) |
29 | 30 | ||
30 | enum INT_TYPE | 31 | enum INT_TYPE |
diff --git a/firmware/target/arm/imx31/dvfs_dptc-imx31.c b/firmware/target/arm/imx31/dvfs_dptc-imx31.c index 02955a5aa4..3f9e9a3dd8 100644 --- a/firmware/target/arm/imx31/dvfs_dptc-imx31.c +++ b/firmware/target/arm/imx31/dvfs_dptc-imx31.c | |||
@@ -75,6 +75,7 @@ static uint32_t check_regulator_setting(uint32_t setting) | |||
75 | 75 | ||
76 | 76 | ||
77 | /** DVFS **/ | 77 | /** DVFS **/ |
78 | #define DVFS_TVWAIT 100 /* Voltage ramp wait time */ | ||
78 | static bool dvfs_running = false; /* Has driver enabled DVFS? */ | 79 | static bool dvfs_running = false; /* Has driver enabled DVFS? */ |
79 | 80 | ||
80 | /* Request tracking since boot */ | 81 | /* Request tracking since boot */ |
@@ -90,10 +91,9 @@ static inline void updten_wait(void) | |||
90 | } | 91 | } |
91 | 92 | ||
92 | /* Do the actual frequency and DVFS pin change - always call with IRQ masked */ | 93 | /* Do the actual frequency and DVFS pin change - always call with IRQ masked */ |
93 | static void do_dvfs_update(unsigned int level) | 94 | static void do_dvfs_update(unsigned long pmcr0, unsigned int level) |
94 | { | 95 | { |
95 | const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level]; | 96 | const struct dvfs_clock_table_entry *setting = &dvfs_clock_table[level]; |
96 | unsigned long pmcr0 = CCM_PMCR0; | ||
97 | 97 | ||
98 | if (pmcr0 & CCM_PMCR0_DPTEN) | 98 | if (pmcr0 & CCM_PMCR0_DPTEN) |
99 | { | 99 | { |
@@ -106,7 +106,7 @@ static void do_dvfs_update(unsigned int level) | |||
106 | pmcr0 &= ~CCM_PMCR0_VSCNT; | 106 | pmcr0 &= ~CCM_PMCR0_VSCNT; |
107 | 107 | ||
108 | if (level < ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) | 108 | if (level < ((pmcr0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) |
109 | { | 109 | { |
110 | pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */ | 110 | pmcr0 |= CCM_PMCR0_UDSC; /* Up scaling, increase */ |
111 | pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS; | 111 | pmcr0 |= setting->vscnt << CCM_PMCR0_VSCNT_POS; |
112 | } | 112 | } |
@@ -136,12 +136,20 @@ static void do_dvfs_update(unsigned int level) | |||
136 | } | 136 | } |
137 | 137 | ||
138 | CCM_PMCR0 = pmcr0; | 138 | CCM_PMCR0 = pmcr0; |
139 | /* Note: changes to frequency with ints unmaked seem to cause spurious | 139 | |
140 | * DVFS interrupts with value CCM_PMCR0_FSVAI_NO_INT. These aren't | 140 | /* dvfs_int_voltage_wait_complete must be call to complete this; how that |
141 | * supposed to happen. Only do the lengthy delay with them enabled. */ | 141 | is accomplished depends upon whether this was an interrupt with DVFS |
142 | enable_irq(); | 142 | enabled or a manual setting of the CPU frequency */ |
143 | udelay(100); /* Software wait for voltage ramp-up */ | 143 | } |
144 | disable_irq(); | 144 | |
145 | /* Perform final DVFS frequency change steps after voltage ramp wait */ | ||
146 | static void dvfs_int_voltage_wait_complete(void) | ||
147 | { | ||
148 | const struct dvfs_clock_table_entry *setting = | ||
149 | &dvfs_clock_table[dvfs_level]; | ||
150 | |||
151 | unsigned long pmcr0 = CCM_PMCR0; | ||
152 | |||
145 | CCM_PDR0 = setting->pdr_val; | 153 | CCM_PDR0 = setting->pdr_val; |
146 | 154 | ||
147 | if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS)) | 155 | if (!(pmcr0 & CCM_PMCR0_DFSUP_POST_DIVIDERS)) |
@@ -155,6 +163,9 @@ static void do_dvfs_update(unsigned int level) | |||
155 | 163 | ||
156 | cpu_frequency = ccm_get_mcu_clk(); | 164 | cpu_frequency = ccm_get_mcu_clk(); |
157 | 165 | ||
166 | if (dvfs_running) | ||
167 | CCM_PMCR0 &= ~CCM_PMCR0_FSVAIM; | ||
168 | |||
158 | if (pmcr0 & CCM_PMCR0_DPTEN) | 169 | if (pmcr0 & CCM_PMCR0_DPTEN) |
159 | { | 170 | { |
160 | update_dptc_counts(); | 171 | update_dptc_counts(); |
@@ -165,24 +176,28 @@ static void do_dvfs_update(unsigned int level) | |||
165 | /* Start DVFS, change the set point and stop it */ | 176 | /* Start DVFS, change the set point and stop it */ |
166 | static void set_current_dvfs_level(unsigned int level) | 177 | static void set_current_dvfs_level(unsigned int level) |
167 | { | 178 | { |
168 | int oldlevel; | ||
169 | |||
170 | /* Have to wait at least 3 div3 clocks before enabling after being | 179 | /* Have to wait at least 3 div3 clocks before enabling after being |
171 | * stopped. */ | 180 | * stopped before calling. */ |
172 | udelay(1500); | ||
173 | 181 | ||
174 | oldlevel = disable_irq_save(); | 182 | updten_wait(); |
183 | |||
184 | int oldlevel = disable_irq_save(); | ||
175 | CCM_PMCR0 |= CCM_PMCR0_DVFEN; | 185 | CCM_PMCR0 |= CCM_PMCR0_DVFEN; |
176 | do_dvfs_update(level); | 186 | do_dvfs_update(CCM_PMCR0, level); |
177 | restore_irq(oldlevel); | 187 | restore_irq(oldlevel); |
178 | 188 | ||
179 | updten_wait(); | 189 | udelay(DVFS_TVWAIT); |
190 | |||
191 | oldlevel = disable_irq_save(); | ||
192 | dvfs_int_voltage_wait_complete(); | ||
193 | restore_irq(oldlevel); | ||
180 | 194 | ||
195 | updten_wait(); | ||
181 | bitclr32(&CCM_PMCR0, CCM_PMCR0_DVFEN); | 196 | bitclr32(&CCM_PMCR0, CCM_PMCR0_DVFEN); |
182 | } | 197 | } |
183 | 198 | ||
184 | /* DVFS Interrupt handler */ | 199 | /* Interrupt handler for DVFS */ |
185 | static void USED_ATTR dvfs_int(void) | 200 | static void dvfs_int(void) |
186 | { | 201 | { |
187 | unsigned long pmcr0 = CCM_PMCR0; | 202 | unsigned long pmcr0 = CCM_PMCR0; |
188 | unsigned long fsvai = pmcr0 & CCM_PMCR0_FSVAI; | 203 | unsigned long fsvai = pmcr0 & CCM_PMCR0_FSVAI; |
@@ -235,14 +250,19 @@ static void USED_ATTR dvfs_int(void) | |||
235 | return; /* Do nothing. Freq change is not required */ | 250 | return; /* Do nothing. Freq change is not required */ |
236 | } /* end switch */ | 251 | } /* end switch */ |
237 | 252 | ||
238 | do_dvfs_update(level); | 253 | /* Mask DVFS interrupt until voltage wait is complete */ |
254 | pmcr0 |= CCM_PMCR0_FSVAIM; | ||
255 | |||
256 | do_dvfs_update(pmcr0, level); | ||
257 | |||
258 | /* Complete this in a few microseconds from now */ | ||
259 | uevent(DVFS_TVWAIT, dvfs_int_voltage_wait_complete); | ||
239 | } | 260 | } |
240 | 261 | ||
241 | /* Interrupt vector for DVFS */ | 262 | /* Interrupt vector for DVFS */ |
242 | static __attribute__((naked, interrupt("IRQ"))) void CCM_DVFS_HANDLER(void) | 263 | static __attribute__((interrupt("IRQ"))) void CCM_DVFS_HANDLER(void) |
243 | { | 264 | { |
244 | /* Audio can glitch with the long udelay if nested IRQ isn't allowed. */ | 265 | dvfs_int(); |
245 | AVIC_NESTED_NI_CALL(dvfs_int, INT_PRIO_DVFS); | ||
246 | } | 266 | } |
247 | 267 | ||
248 | /* Initialize the DVFS hardware */ | 268 | /* Initialize the DVFS hardware */ |
@@ -273,7 +293,7 @@ static void INIT_ATTR dvfs_init(void) | |||
273 | 293 | ||
274 | /* GP load bits disabled */ | 294 | /* GP load bits disabled */ |
275 | bitclr32(&CCM_PMCR1, 0xf); | 295 | bitclr32(&CCM_PMCR1, 0xf); |
276 | 296 | ||
277 | /* Initialize DVFS signal weights and detection modes. */ | 297 | /* Initialize DVFS signal weights and detection modes. */ |
278 | int i; | 298 | int i; |
279 | for (i = 0; i < 16; i++) | 299 | for (i = 0; i < 16; i++) |
@@ -312,6 +332,7 @@ static void INIT_ATTR dvfs_init(void) | |||
312 | dvfs_clock_table[DVFS_LEVEL_DEFAULT].pdr_val); | 332 | dvfs_clock_table[DVFS_LEVEL_DEFAULT].pdr_val); |
313 | 333 | ||
314 | /* Set initial level and working point. */ | 334 | /* Set initial level and working point. */ |
335 | udelay(1500); | ||
315 | set_current_dvfs_level(DVFS_LEVEL_DEFAULT); | 336 | set_current_dvfs_level(DVFS_LEVEL_DEFAULT); |
316 | 337 | ||
317 | logf("DVFS: Initialized"); | 338 | logf("DVFS: Initialized"); |
@@ -537,33 +558,23 @@ void dvfs_stop(void) | |||
537 | if (!dvfs_running) | 558 | if (!dvfs_running) |
538 | return; | 559 | return; |
539 | 560 | ||
561 | uevent_cancel(); | ||
562 | |||
540 | /* Mask DVFS interrupts. */ | 563 | /* Mask DVFS interrupts. */ |
541 | avic_disable_int(INT_CCM_DVFS); | 564 | avic_disable_int(INT_CCM_DVFS); |
542 | bitset32(&CCM_PMCR0, CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI); | 565 | bitset32(&CCM_PMCR0, CCM_PMCR0_FSVAIM | CCM_PMCR0_LBMI); |
543 | 566 | ||
544 | if (((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS) != | ||
545 | DVFS_LEVEL_DEFAULT) | ||
546 | { | ||
547 | int oldlevel; | ||
548 | /* Set default frequency level */ | ||
549 | updten_wait(); | ||
550 | oldlevel = disable_irq_save(); | ||
551 | do_dvfs_update(DVFS_LEVEL_DEFAULT); | ||
552 | restore_irq(oldlevel); | ||
553 | updten_wait(); | ||
554 | } | ||
555 | |||
556 | /* Disable DVFS. */ | ||
557 | bitclr32(&CCM_PMCR0, CCM_PMCR0_DVFEN); | ||
558 | dvfs_running = false; | 567 | dvfs_running = false; |
559 | 568 | ||
569 | /* Set default frequency level */ | ||
570 | set_current_dvfs_level(DVFS_LEVEL_DEFAULT); | ||
560 | logf("DVFS: stopped"); | 571 | logf("DVFS: stopped"); |
561 | } | 572 | } |
562 | 573 | ||
563 | /* Is DVFS enabled? */ | 574 | /* Is DVFS enabled? */ |
564 | bool dvfs_enabled(void) | 575 | bool dvfs_enabled(void) |
565 | { | 576 | { |
566 | return dvfs_running; | 577 | return dvfs_running; |
567 | } | 578 | } |
568 | 579 | ||
569 | /* If DVFS is disabled, set the level explicitly */ | 580 | /* If DVFS is disabled, set the level explicitly */ |
@@ -575,6 +586,7 @@ void dvfs_set_level(unsigned int level) | |||
575 | level == ((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) | 586 | level == ((CCM_PMCR0 & CCM_PMCR0_DVSUP) >> CCM_PMCR0_DVSUP_POS)) |
576 | return; | 587 | return; |
577 | 588 | ||
589 | udelay(1500); | ||
578 | set_current_dvfs_level(level); | 590 | set_current_dvfs_level(level); |
579 | } | 591 | } |
580 | 592 | ||
@@ -778,7 +790,7 @@ void dptc_start(void) | |||
778 | enable_dptc(); | 790 | enable_dptc(); |
779 | restore_irq(oldlevel); | 791 | restore_irq(oldlevel); |
780 | 792 | ||
781 | avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC, | 793 | avic_enable_int(INT_CCM_CLK, INT_TYPE_IRQ, INT_PRIO_DPTC, |
782 | CCM_CLK_HANDLER); | 794 | CCM_CLK_HANDLER); |
783 | 795 | ||
784 | logf("DPTC: started"); | 796 | logf("DPTC: started"); |
diff --git a/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c index f30287d4e9..491bd52a66 100644 --- a/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/kernel-gigabeat-s.c | |||
@@ -56,7 +56,7 @@ void INIT_ATTR tick_start(unsigned int interval_in_ms) | |||
56 | EPITCR1 = EPITCR_CLKSRC_IPG_CLK | EPITCR_WAITEN | EPITCR_IOVW | | 56 | EPITCR1 = EPITCR_CLKSRC_IPG_CLK | EPITCR_WAITEN | EPITCR_IOVW | |
57 | ((2640-1) << EPITCR_PRESCALER_POS) | EPITCR_RLD | | 57 | ((2640-1) << EPITCR_PRESCALER_POS) | EPITCR_RLD | |
58 | EPITCR_OCIEN | EPITCR_ENMOD; | 58 | EPITCR_OCIEN | EPITCR_ENMOD; |
59 | 59 | ||
60 | EPITLR1 = interval_in_ms*25; /* Count down from interval */ | 60 | EPITLR1 = interval_in_ms*25; /* Count down from interval */ |
61 | EPITCMPR1 = 0; /* Event when counter reaches 0 */ | 61 | EPITCMPR1 = 0; /* Event when counter reaches 0 */ |
62 | EPITSR1 = EPITSR_OCIF; /* Clear any pending interrupt */ | 62 | EPITSR1 = EPITSR_OCIF; /* Clear any pending interrupt */ |
@@ -86,9 +86,3 @@ void tick_stop(void) | |||
86 | EPITSR1 = EPITSR_OCIF; /* Clear pending */ | 86 | EPITSR1 = EPITSR_OCIF; /* Clear pending */ |
87 | ccm_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */ | 87 | ccm_module_clock_gating(CG_EPIT1, CGM_OFF); /* Turn off module clock */ |
88 | } | 88 | } |
89 | |||
90 | |||
91 | void kernel_audio_locking(bool locking) | ||
92 | { | ||
93 | dvfs_int_mask(locking); | ||
94 | } \ No newline at end of file | ||
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c index 76789a7dbd..7304bdcff3 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-gigabeat-s.c | |||
@@ -104,7 +104,7 @@ static void play_dma_callback(void) | |||
104 | PCM_DMAST_ERR_DMA : PCM_DMAST_OK; | 104 | PCM_DMAST_ERR_DMA : PCM_DMAST_OK; |
105 | const void *addr; | 105 | const void *addr; |
106 | size_t size; | 106 | size_t size; |
107 | 107 | ||
108 | if (pcm_play_dma_complete_callback(status, &addr, &size)) | 108 | if (pcm_play_dma_complete_callback(status, &addr, &size)) |
109 | { | 109 | { |
110 | play_start_dma(addr, size); | 110 | play_start_dma(addr, size); |
@@ -114,33 +114,20 @@ static void play_dma_callback(void) | |||
114 | 114 | ||
115 | void pcm_play_lock(void) | 115 | void pcm_play_lock(void) |
116 | { | 116 | { |
117 | /* Need to prevent DVFS from causing interrupt priority inversion if audio | ||
118 | * is locked and a DVFS interrupt fires, blocking reenabling of audio by a | ||
119 | * low-priority mode for at least the duration of the lengthy DVFS routine. | ||
120 | * Not really an issue with state changes but lockout when playing. | ||
121 | * | ||
122 | * Keep direct use of DVFS code away from here though. This could provide | ||
123 | * more services in the future anyway. */ | ||
124 | kernel_audio_locking(true); | ||
125 | ++dma_play_data.locked; | 117 | ++dma_play_data.locked; |
126 | } | 118 | } |
127 | 119 | ||
128 | void pcm_play_unlock(void) | 120 | void pcm_play_unlock(void) |
129 | { | 121 | { |
130 | if (--dma_play_data.locked == 0) | 122 | if (--dma_play_data.locked == 0 && dma_play_data.state != 0) |
131 | { | 123 | { |
132 | if (dma_play_data.state != 0) | 124 | int oldstatus = disable_irq_save(); |
133 | { | 125 | int pending = dma_play_data.callback_pending; |
134 | int oldstatus = disable_irq_save(); | 126 | dma_play_data.callback_pending = 0; |
135 | int pending = dma_play_data.callback_pending; | 127 | restore_irq(oldstatus); |
136 | dma_play_data.callback_pending = 0; | 128 | |
137 | restore_irq(oldstatus); | 129 | if (pending != 0) |
138 | 130 | play_dma_callback(); | |
139 | if (pending != 0) | ||
140 | play_dma_callback(); | ||
141 | } | ||
142 | |||
143 | kernel_audio_locking(false); | ||
144 | } | 131 | } |
145 | } | 132 | } |
146 | 133 | ||
@@ -368,26 +355,20 @@ static void rec_dma_callback(void) | |||
368 | 355 | ||
369 | void pcm_rec_lock(void) | 356 | void pcm_rec_lock(void) |
370 | { | 357 | { |
371 | kernel_audio_locking(true); | ||
372 | ++dma_rec_data.locked; | 358 | ++dma_rec_data.locked; |
373 | } | 359 | } |
374 | 360 | ||
375 | void pcm_rec_unlock(void) | 361 | void pcm_rec_unlock(void) |
376 | { | 362 | { |
377 | if (--dma_rec_data.locked == 0) | 363 | if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) |
378 | { | 364 | { |
379 | if (dma_rec_data.state != 0) | 365 | int oldstatus = disable_irq_save(); |
380 | { | 366 | int pending = dma_rec_data.callback_pending; |
381 | int oldstatus = disable_irq_save(); | 367 | dma_rec_data.callback_pending = 0; |
382 | int pending = dma_rec_data.callback_pending; | 368 | restore_irq(oldstatus); |
383 | dma_rec_data.callback_pending = 0; | 369 | |
384 | restore_irq(oldstatus); | 370 | if (pending != 0) |
385 | 371 | rec_dma_callback(); | |
386 | if (pending != 0) | ||
387 | rec_dma_callback(); | ||
388 | } | ||
389 | |||
390 | kernel_audio_locking(false); | ||
391 | } | 372 | } |
392 | } | 373 | } |
393 | 374 | ||
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c index 5c70411c30..545905d4c2 100644 --- a/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/system-gigabeat-s.c | |||
@@ -84,7 +84,62 @@ void watchdog_service(void) | |||
84 | WDOG_WSR = 0xaaaa; | 84 | WDOG_WSR = 0xaaaa; |
85 | } | 85 | } |
86 | 86 | ||
87 | /** GPT timer routines - basis for udelay **/ | 87 | |
88 | /** uevent APIs **/ | ||
89 | |||
90 | static void (*ucallback)(void) = NULL; /* uevent callback */ | ||
91 | |||
92 | static void cancel_uevent(void) | ||
93 | { | ||
94 | GPTSR = GPTSR_OF1; | ||
95 | GPTIR &= ~GPTIR_OF1IE; | ||
96 | ucallback = NULL; | ||
97 | } | ||
98 | |||
99 | static void __attribute__((interrupt("IRQ"))) GPT_HANDLER(void) | ||
100 | { | ||
101 | uevent_cb_type cb = ucallback; | ||
102 | cancel_uevent(); | ||
103 | cb(); | ||
104 | } | ||
105 | |||
106 | void uevent(unsigned long usecs, uevent_cb_type callback) | ||
107 | { | ||
108 | if (!callback || ucallback) | ||
109 | return; /* Is busy */ | ||
110 | |||
111 | unsigned long status = disable_interrupt_save(IRQ_FIQ_STATUS); | ||
112 | |||
113 | ucallback = callback; | ||
114 | |||
115 | for (int i = 0; i < 1; i++) | ||
116 | { | ||
117 | unsigned long utime = GPTCNT; | ||
118 | unsigned long time = utime + usecs + 1; | ||
119 | |||
120 | GPTOCR1 = time; | ||
121 | GPTSR = GPTSR_OF1; | ||
122 | GPTIR |= GPTIR_OF1IE; | ||
123 | |||
124 | if (TIME_BEFORE(GPTCNT, time)) | ||
125 | break; /* Didn't miss it */ | ||
126 | } | ||
127 | |||
128 | restore_interrupt(status); | ||
129 | } | ||
130 | |||
131 | void uevent_cancel(void) | ||
132 | { | ||
133 | unsigned long status = disable_interrupt_save(IRQ_FIQ_STATUS); | ||
134 | |||
135 | if (ucallback) | ||
136 | cancel_uevent(); | ||
137 | |||
138 | restore_interrupt(status); | ||
139 | } | ||
140 | |||
141 | |||
142 | /** GPT timer routines - basis for udelay/uevent **/ | ||
88 | 143 | ||
89 | /* Start the general-purpose timer (1MHz) */ | 144 | /* Start the general-purpose timer (1MHz) */ |
90 | void gpt_start(void) | 145 | void gpt_start(void) |
@@ -102,13 +157,20 @@ void gpt_start(void) | |||
102 | */ | 157 | */ |
103 | GPTCR = GPTCR_FRR | GPTCR_WAITEN | GPTCR_CLKSRC_IPG_CLK; | 158 | GPTCR = GPTCR_FRR | GPTCR_WAITEN | GPTCR_CLKSRC_IPG_CLK; |
104 | GPTPR = ipg_mhz - 1; | 159 | GPTPR = ipg_mhz - 1; |
160 | GPTSR = GPTSR_OF1; | ||
105 | GPTCR |= GPTCR_EN; | 161 | GPTCR |= GPTCR_EN; |
162 | |||
163 | avic_enable_int(INT_GPT, INT_TYPE_IRQ, INT_PRIO_GPT, GPT_HANDLER); | ||
106 | } | 164 | } |
107 | 165 | ||
108 | /* Stop the general-purpose timer */ | 166 | /* Stop the general-purpose timer */ |
109 | void gpt_stop(void) | 167 | void gpt_stop(void) |
110 | { | 168 | { |
169 | unsigned long status = disable_interrupt_save(IRQ_FIQ_STATUS); | ||
170 | avic_disable_int(INT_GPT); | ||
171 | cancel_uevent(); | ||
111 | GPTCR &= ~GPTCR_EN; | 172 | GPTCR &= ~GPTCR_EN; |
173 | restore_interrupt(status); | ||
112 | } | 174 | } |
113 | 175 | ||
114 | int system_memory_guard(int newmode) | 176 | int system_memory_guard(int newmode) |
@@ -258,7 +320,7 @@ void dumpregs(void) | |||
258 | "mov %3,r12": | 320 | "mov %3,r12": |
259 | "=r"(regs.r8),"=r"(regs.r9), | 321 | "=r"(regs.r8),"=r"(regs.r9), |
260 | "=r"(regs.r10),"=r"(regs.r11):); | 322 | "=r"(regs.r10),"=r"(regs.r11):); |
261 | 323 | ||
262 | asm volatile ("mov %0,r12\n\t" | 324 | asm volatile ("mov %0,r12\n\t" |
263 | "mov %1,sp\n\t" | 325 | "mov %1,sp\n\t" |
264 | "mov %2,lr\n\t" | 326 | "mov %2,lr\n\t" |
@@ -280,7 +342,7 @@ void dumpregs(void) | |||
280 | DEBUGF("R8=0x%x\tR9=0x%x\tR10=0x%x\tR11=0x%x\n",regs.r8,regs.r9,regs.r10,regs.r11); | 342 | DEBUGF("R8=0x%x\tR9=0x%x\tR10=0x%x\tR11=0x%x\n",regs.r8,regs.r9,regs.r10,regs.r11); |
281 | DEBUGF("R12=0x%x\tSP=0x%x\tLR=0x%x\tPC=0x%x\n",regs.r12,regs.sp,regs.lr,regs.pc); | 343 | DEBUGF("R12=0x%x\tSP=0x%x\tLR=0x%x\tPC=0x%x\n",regs.r12,regs.sp,regs.lr,regs.pc); |
282 | //DEBUGF("CPSR=0x%x\t\n",regs.cpsr); | 344 | //DEBUGF("CPSR=0x%x\t\n",regs.cpsr); |
283 | 345 | ||
284 | } | 346 | } |
285 | 347 | ||
286 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 348 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
diff --git a/firmware/target/arm/imx31/gigabeat-s/system-target.h b/firmware/target/arm/imx31/gigabeat-s/system-target.h index 44a97c7ca9..b12ca13bb5 100644 --- a/firmware/target/arm/imx31/gigabeat-s/system-target.h +++ b/firmware/target/arm/imx31/gigabeat-s/system-target.h | |||
@@ -44,6 +44,11 @@ static inline unsigned long usec_timer(void) | |||
44 | return GPTCNT; | 44 | return GPTCNT; |
45 | } | 45 | } |
46 | 46 | ||
47 | /* Fire an event usecs microseconds from now */ | ||
48 | typedef void (* uevent_cb_type)(void); | ||
49 | void uevent(unsigned long usecs, uevent_cb_type callback); | ||
50 | void uevent_cancel(void); | ||
51 | |||
47 | void watchdog_init(unsigned int half_seconds); | 52 | void watchdog_init(unsigned int half_seconds); |
48 | void watchdog_service(void); | 53 | void watchdog_service(void); |
49 | 54 | ||
@@ -58,9 +63,6 @@ void tick_stop(void); | |||
58 | void kernel_device_init(void); | 63 | void kernel_device_init(void); |
59 | void system_halt(void); | 64 | void system_halt(void); |
60 | 65 | ||
61 | /* Handle some audio lockout related tasks */ | ||
62 | void kernel_audio_locking(bool locking); | ||
63 | |||
64 | #define KDEV_INIT | 66 | #define KDEV_INIT |
65 | 67 | ||
66 | struct ARM_REGS { | 68 | struct ARM_REGS { |