diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2017-01-21 08:04:43 -0500 |
---|---|---|
committer | Gerrit Rockbox <gerrit@rockbox.org> | 2017-01-25 00:05:13 +0100 |
commit | 783c77531c35e62dd754c510c4f2beefe6df4a9d (patch) | |
tree | 79e747eb1ba3716b56e5178145536f316c44e403 /firmware/target | |
parent | a1d1832049146925400b57c4cd81b0739b674971 (diff) | |
download | rockbox-783c77531c35e62dd754c510c4f2beefe6df4a9d.tar.gz rockbox-783c77531c35e62dd754c510c4f2beefe6df4a9d.zip |
AMS: Return ascodec to interrupt-based I2C2 driver
1. Slightly revised and regularized internal interface. Callback is used
for read and write to provide completion signal instead of having two
mechanisms.
2. Lower overhead for asynchronous or alterate completion callbacks. We
now only init what is required by the transfer. A couple unneeded
structure members were also nixed.
3. Fixes a bug that would neglect a semaphore wait if pumping the I2C
interrupts in a loop when not in thread state or interrupts are masked.
4. Corrects broken initialization order by defining KDEV_INIT, which
makes kernel_init() call kernel_device_init() to initialize additional
devices _after_ the kernel, threading and synchronization objects are
safe to use.
5. Locking set_cpu_frequency has to be done at the highest level in
system.c to ensure the boost counter and the frequency are both set in
agreement. Reconcile the locking inteface between PP and AMS (the only
two currently using locking there) to keep it clean.
Now works fine with voltages in GIT HEAD on my Fuze v2, type 0.
Previously, everything crashed and died instantly. action.c calling
set_cpu_frequency from a tick was part of it. The rest may have been
related to 3. and 4. Honestly, I'm not certain!
Testing by Mihail Zenkov indicates it solves our problems. This will
get the developer builds running again after the kernel assert code
push.
Change-Id: Ie245994fb3e318dd5ef48e383ce61fdd977224d4
Diffstat (limited to 'firmware/target')
-rw-r--r-- | firmware/target/arm/as3525/ascodec-as3525.c | 571 | ||||
-rw-r--r-- | firmware/target/arm/as3525/debug-as3525.c | 30 | ||||
-rw-r--r-- | firmware/target/arm/as3525/system-as3525.c | 37 | ||||
-rw-r--r-- | firmware/target/arm/as3525/system-target.h | 21 | ||||
-rw-r--r-- | firmware/target/arm/pp/system-pp5002.c | 19 | ||||
-rw-r--r-- | firmware/target/arm/pp/system-pp502x.c | 21 | ||||
-rw-r--r-- | firmware/target/arm/pp/system-target.h | 17 |
7 files changed, 583 insertions, 133 deletions
diff --git a/firmware/target/arm/as3525/ascodec-as3525.c b/firmware/target/arm/as3525/ascodec-as3525.c index ec25a415a5..e144f07ed4 100644 --- a/firmware/target/arm/as3525/ascodec-as3525.c +++ b/firmware/target/arm/as3525/ascodec-as3525.c | |||
@@ -50,7 +50,7 @@ | |||
50 | #include "system.h" | 50 | #include "system.h" |
51 | #include "as3525.h" | 51 | #include "as3525.h" |
52 | #include "i2c.h" | 52 | #include "i2c.h" |
53 | #include "logf.h" | 53 | #include <linked_list.h> |
54 | 54 | ||
55 | #define I2C2_DATA *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x00)) | 55 | #define I2C2_DATA *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x00)) |
56 | #define I2C2_SLAD0 *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x04)) | 56 | #define I2C2_SLAD0 *((volatile unsigned int *)(I2C_AUDIO_BASE + 0x04)) |
@@ -78,234 +78,495 @@ | |||
78 | #define I2C2_IRQ_RXOVER 0x10 | 78 | #define I2C2_IRQ_RXOVER 0x10 |
79 | #define I2C2_IRQ_ACKTIMEO 0x80 | 79 | #define I2C2_IRQ_ACKTIMEO 0x80 |
80 | 80 | ||
81 | #define REQ_UNFINISHED 0 | ||
82 | #define REQ_FINISHED 1 | ||
83 | #define REQ_RETRY 2 | ||
81 | 84 | ||
82 | static struct mutex as_mtx; | 85 | #ifdef DEBUG |
83 | 86 | #define IFDEBUG(x) x | |
84 | #if CONFIG_CHARGING | 87 | #else |
85 | static bool chg_status = false; | 88 | #define IFDEBUG(x) |
86 | static bool endofch = false; | ||
87 | #endif | 89 | #endif |
88 | 90 | ||
89 | /* returns true when busy */ | 91 | #define ASCODEC_REQ_READ 0 |
90 | static inline bool i2c_busy(void) | 92 | #define ASCODEC_REQ_WRITE 1 |
93 | |||
94 | /* | ||
95 | * How many bytes we using in struct ascodec_request for the data buffer. | ||
96 | * 4 fits the alignment best right now. | ||
97 | * We don't actually use more than 3 at the moment (when reading interrupts) | ||
98 | * Upper limit would be 255 since DACNT is 8 bits! | ||
99 | */ | ||
100 | #define ASCODEC_REQ_MAXLEN 4 | ||
101 | |||
102 | struct ascodec_request; | ||
103 | typedef void (ascodec_cb_fn)(struct ascodec_request *req); | ||
104 | |||
105 | struct ascodec_request { | ||
106 | /* standard request members */ | ||
107 | struct ll_node node; /* request list link (first!) */ | ||
108 | unsigned char type; /* reqest type (read or write) */ | ||
109 | unsigned char index; /* initial i2c sub address */ | ||
110 | unsigned char cnt; /* bytes remaining */ | ||
111 | unsigned char data[ASCODEC_REQ_MAXLEN]; /* actual I/O data */ | ||
112 | |||
113 | /* members relevant when a callback is specified (callback != NULL) */ | ||
114 | ascodec_cb_fn *callback; /* pointer to callback function */ | ||
115 | intptr_t cbdata; /* data for callback function */ | ||
116 | int len_done; /* amount actually transferred */ | ||
117 | }; | ||
118 | |||
119 | /* I2C driver data */ | ||
120 | static struct mutex as_mtx; | ||
121 | static struct ll_head req_list; | ||
122 | static unsigned char *req_data_ptr; | ||
123 | #define REQ_FIRST ((struct ascodec_request *)req_list.head) | ||
124 | |||
125 | /* INT_AUDIO interrupt data */ | ||
126 | static void ascodec_int_audio_cb(struct ascodec_request *req); | ||
127 | void INT_I2C_AUDIO(void); | ||
128 | static struct ascodec_request as_audio_req; | ||
129 | static struct semaphore adc_done_sem; | ||
130 | static unsigned long ascodec_enrd0_shadow = 0; | ||
131 | |||
132 | static void ascodec_wait_cb(struct ascodec_request *req); | ||
133 | |||
134 | /** --debugging help-- **/ | ||
135 | |||
136 | #ifdef DEBUG | ||
137 | /* counters for debugging INT_AUDIO */ | ||
138 | static struct int_audio_counters { | ||
139 | int int_audio; | ||
140 | int int_chg_finished; | ||
141 | int int_chg_insert; | ||
142 | int int_chg_remove; | ||
143 | int int_usb_insert; | ||
144 | int int_usb_remove; | ||
145 | int int_rtc; | ||
146 | int int_adc; | ||
147 | } int_audio_counters; | ||
148 | #endif /* DEBUG */ | ||
149 | |||
150 | #define COUNT_INT(x) IFDEBUG((int_audio_counters.int_##x)++) | ||
151 | |||
152 | |||
153 | /** --stock request and callback functionality -- **/ | ||
154 | |||
155 | /* init for common request data (call before submitting) */ | ||
156 | static inline void ascodec_req_init(struct ascodec_request *req, int type, | ||
157 | unsigned int index, unsigned int cnt) | ||
91 | { | 158 | { |
92 | return (I2C2_SR & 1); | 159 | req->type = type; |
160 | req->index = index; | ||
161 | req->cnt = cnt; | ||
93 | } | 162 | } |
94 | 163 | ||
95 | void i2c_init(void) | 164 | /* stock no-wait init for request (use any callback and data) */ |
165 | static inline void ascodec_async_init(struct ascodec_request *req, | ||
166 | ascodec_cb_fn *callback, intptr_t cbdata) | ||
96 | { | 167 | { |
168 | /* cbdata is unused if no callback is used */ | ||
169 | if ((req->callback = callback)) | ||
170 | req->cbdata = cbdata; | ||
97 | } | 171 | } |
98 | 172 | ||
99 | static void i2c2_init(void) | 173 | /* initialize the stock completion callback */ |
174 | static inline void ascodec_wait_init(struct ascodec_request *req, | ||
175 | struct semaphore *completep) | ||
100 | { | 176 | { |
101 | int prescaler; | 177 | req->callback = ascodec_wait_cb; |
102 | 178 | req->cbdata = (intptr_t)completep; | |
103 | /* prescaler for i2c clock */ | 179 | semaphore_init(completep, 1, 0); |
104 | prescaler = AS3525_I2C_PRESCALER; | 180 | } |
105 | I2C2_CPSR0 = prescaler & 0xFF; /* 8 lsb */ | ||
106 | I2C2_CPSR1 = (prescaler >> 8) & 0x3; /* 2 msb */ | ||
107 | 181 | ||
108 | /* set i2c slave address of codec part */ | 182 | /* caller waits here when using ascodec_wait_cb to do synchronous transfers */ |
109 | I2C2_SLAD0 = AS3514_I2C_ADDR << 1; | 183 | static void ascodec_wait(struct ascodec_request *req) |
184 | { | ||
185 | struct semaphore *completep = (struct semaphore *)req->cbdata; | ||
186 | int timeout = TIMEOUT_BLOCK; | ||
110 | 187 | ||
111 | I2C2_CNTRL = I2C2_CNTRL_DEFAULT; | 188 | if (!irq_enabled() || !is_thread_context()) { |
189 | timeout = TIMEOUT_NOBLOCK; /* poll semaphore, no block */ | ||
190 | } | ||
112 | 191 | ||
192 | while (semaphore_wait(completep, timeout) == OBJ_WAIT_TIMEDOUT) { | ||
193 | /* pump the i2c interrupts ourselves (only waiting can do this!) */ | ||
194 | if (I2C2_MIS) { | ||
195 | INT_I2C_AUDIO(); | ||
196 | } | ||
197 | } | ||
113 | } | 198 | } |
114 | 199 | ||
115 | /* initialises the internal i2c bus and prepares for transfers to the codec */ | 200 | /* stock callback used in order to wait for a transfer to complete */ |
116 | void ascodec_init(void) | 201 | static void ascodec_wait_cb(struct ascodec_request *req) |
117 | { | 202 | { |
203 | struct semaphore *completep = (struct semaphore *)req->cbdata; | ||
204 | semaphore_release(completep); | ||
205 | } | ||
118 | 206 | ||
119 | mutex_init(&as_mtx); | 207 | |
208 | /**-- I2C2 interrupt handling --**/ | ||
209 | |||
210 | /* start the controller on the next transfer */ | ||
211 | static void ascodec_start_req(struct ascodec_request *req) | ||
212 | { | ||
213 | int unmask = 0; | ||
120 | 214 | ||
121 | /* enable clock */ | 215 | /* enable clock */ |
122 | bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE); | 216 | bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE); |
123 | 217 | ||
124 | i2c2_init(); | 218 | /* start transfer */ |
219 | I2C2_SADDR = req->index; | ||
220 | |||
221 | if (req->type == ASCODEC_REQ_READ) { | ||
222 | req_data_ptr = req->data; | ||
223 | I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_READ; | ||
224 | unmask = I2C2_IRQ_RXFULL|I2C2_IRQ_RXOVER; | ||
225 | } else { | ||
226 | req_data_ptr = &req->data[1]; | ||
227 | I2C2_CNTRL = I2C2_CNTRL_DEFAULT | I2C2_CNTRL_WRITE; | ||
228 | I2C2_DATA = req->data[0]; | ||
229 | unmask = I2C2_IRQ_TXEMPTY|I2C2_IRQ_ACKTIMEO; | ||
230 | } | ||
125 | 231 | ||
126 | /* Generate irq for usb+charge status change */ | 232 | I2C2_DACNT = req->cnt; |
127 | ascodec_write(AS3514_IRQ_ENRD0, | 233 | I2C2_IMR |= unmask; /* enable interrupts */ |
128 | #if CONFIG_CHARGING /* m200v4 can't charge */ | 234 | } |
129 | IRQ_CHGSTAT | IRQ_ENDOFCH | | ||
130 | #endif | ||
131 | IRQ_USBSTAT); | ||
132 | 235 | ||
133 | #if CONFIG_CPU == AS3525v2 | 236 | /* send the next bytes or read bytes received */ |
134 | /* XIRQ = IRQ, active low reset signal, 6mA push-pull output */ | 237 | static int ascodec_continue_req(struct ascodec_request *req, int irq_status) |
135 | ascodec_write_pmu(0x1a, 3, (1<<2)|3); /* 1A-3 = Out_Cntr3 register */ | 238 | { |
136 | /* reset for compatible with old bootloader */ | 239 | if ((irq_status & (I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO)) > 0) { |
137 | ascodec_write(AS3514_IRQ_ENRD2, 0x0); | 240 | /* some error occured, restart the request */ |
138 | #else | 241 | return REQ_RETRY; |
139 | /* Generate irq for push-pull, active high, irq on rtc+adc change */ | 242 | } |
140 | ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE); | ||
141 | #endif | ||
142 | 243 | ||
143 | VIC_INT_ENABLE = INTERRUPT_AUDIO; | 244 | if (req->type == ASCODEC_REQ_READ && |
245 | (irq_status & I2C2_IRQ_RXFULL) > 0) { | ||
246 | *req_data_ptr++ = I2C2_DATA; | ||
247 | } else { | ||
248 | if (req->cnt > 1 && | ||
249 | (irq_status & I2C2_IRQ_TXEMPTY) > 0) { | ||
250 | I2C2_DATA = *req_data_ptr++; | ||
251 | } | ||
252 | } | ||
144 | 253 | ||
145 | /* detect if USB was connected at startup since there is no transition */ | 254 | req->index++; |
146 | int data = ascodec_read(AS3514_IRQ_ENRD0); | 255 | if (--req->cnt > 0) |
256 | return REQ_UNFINISHED; | ||
147 | 257 | ||
148 | if(data & USB_STATUS) | 258 | return REQ_FINISHED; |
149 | usb_insert_int(); | 259 | } |
150 | 260 | ||
151 | #if CONFIG_CHARGING | 261 | /* complete the request and call the completion callback, if any */ |
152 | chg_status = data & CHG_STATUS; | 262 | static void ascodec_finish_req(struct ascodec_request *req) |
153 | #endif | 263 | { |
264 | /* | ||
265 | * Wait if still busy, unfortunately this happens since | ||
266 | * the controller is running at a low divisor, so it's | ||
267 | * still busy when we serviced the interrupt. | ||
268 | * I tried upping the i2c speed to 4MHz which | ||
269 | * made the number of busywait cycles much smaller | ||
270 | * (none for reads and only a few for writes), | ||
271 | * but who knows if it's reliable at that frequency. ;) | ||
272 | * For one thing, 8MHz doesn't work, so 4MHz is likely | ||
273 | * borderline. | ||
274 | * In general writes need much more wait cycles than reads | ||
275 | * for some reason, possibly because we read the data register | ||
276 | * for reads, which will likely block the processor while | ||
277 | * the i2c controller responds to the register read. | ||
278 | */ | ||
279 | while (I2C2_SR & 1); | ||
280 | |||
281 | if (req->callback) { | ||
282 | req->len_done = req_data_ptr - req->data; | ||
283 | req->callback(req); | ||
284 | } | ||
154 | } | 285 | } |
155 | 286 | ||
156 | /* returns false if transfer incomplete */ | 287 | /* ISR for I2C2 */ |
157 | static bool i2c2_transfer(void) | 288 | void INT_I2C_AUDIO(void) |
158 | { | 289 | { |
159 | static int try = 0; | 290 | struct ascodec_request *req = REQ_FIRST; |
160 | 291 | ||
161 | /* wait for transfer*/ | 292 | int irq_status = I2C2_MIS; |
162 | int i = 10000; | 293 | int status = ascodec_continue_req(req, irq_status); |
163 | while (I2C2_DACNT != 0 && i--); | ||
164 | 294 | ||
165 | if (!i) { | 295 | I2C2_INT_CLR = irq_status; /* clear interrupt status */ |
166 | if (try == 5) | ||
167 | panicf("I2C2 reset failed"); | ||
168 | 296 | ||
169 | logf("reset I2C2 %d", try); | 297 | if (status != REQ_UNFINISHED) { |
298 | /* mask rx/tx interrupts */ | ||
299 | I2C2_IMR &= ~(I2C2_IRQ_TXEMPTY|I2C2_IRQ_RXFULL| | ||
300 | I2C2_IRQ_RXOVER|I2C2_IRQ_ACKTIMEO); | ||
170 | 301 | ||
171 | i2c2_init(); | 302 | if (status == REQ_FINISHED) |
303 | ascodec_finish_req(req); | ||
172 | 304 | ||
173 | try++; | 305 | int oldlevel = disable_irq_save(); /* IRQs are stacked */ |
174 | return false; | 306 | |
175 | } | 307 | /* |
308 | * If status == REQ_RETRY, this will restart the request from where | ||
309 | * it left off because we didn't remove it from the request list | ||
310 | */ | ||
176 | 311 | ||
177 | try = 0; | 312 | if (status == REQ_FINISHED) { |
178 | return true; | 313 | ll_remove_next(&req_list, NULL); |
314 | } | ||
315 | |||
316 | req = REQ_FIRST; | ||
317 | if (req == NULL) { | ||
318 | /* disable clock */ | ||
319 | bitclr32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE); | ||
320 | } else { | ||
321 | ascodec_start_req(req); | ||
322 | } | ||
323 | |||
324 | restore_irq(oldlevel); | ||
325 | } | ||
179 | } | 326 | } |
180 | 327 | ||
181 | void ascodec_write(unsigned int index, unsigned int value) | 328 | |
329 | /** --Routines for reading and writing data on the bus-- **/ | ||
330 | |||
331 | /* add the request to the queue */ | ||
332 | static void ascodec_submit(struct ascodec_request *req) | ||
182 | { | 333 | { |
183 | ascodec_lock(); | 334 | int oldlevel = disable_irq_save(); |
335 | |||
336 | ll_insert_last(&req_list, &req->node); | ||
337 | |||
338 | if (REQ_FIRST == req) { | ||
339 | /* first on list? start driver */ | ||
340 | ascodec_start_req(req); | ||
341 | } | ||
342 | |||
343 | restore_irq(oldlevel); | ||
344 | } | ||
184 | 345 | ||
346 | /* | ||
347 | * The request struct passed in must be allocated statically. | ||
348 | * If you call ascodec_async_write from different places, each | ||
349 | * call needs it's own request struct. | ||
350 | */ | ||
351 | static void ascodec_async_write(struct ascodec_request *req, | ||
352 | unsigned int index, unsigned int value) | ||
353 | { | ||
185 | #ifndef HAVE_AS3543 | 354 | #ifndef HAVE_AS3543 |
186 | if (index == AS3514_CVDD_DCDC3) /* prevent setting of the LREG_CP_not bit */ | 355 | if (index == AS3514_CVDD_DCDC3) /* prevent setting of the LREG_CP_not bit */ |
187 | value &= ~(1 << 5); | 356 | value &= ~(1 << 5); |
188 | #endif | 357 | #endif |
189 | 358 | ||
190 | do { | 359 | ascodec_req_init(req, ASCODEC_REQ_WRITE, index, 1); |
191 | /* wait if still busy */ | 360 | req->data[0] = value; |
192 | while (i2c_busy()); | 361 | ascodec_submit(req); |
362 | } | ||
363 | |||
364 | void ascodec_write(unsigned int index, unsigned int value) | ||
365 | { | ||
366 | struct ascodec_request req; | ||
367 | struct semaphore complete; | ||
193 | 368 | ||
194 | /* start transfer */ | 369 | ascodec_wait_init(&req, &complete); |
195 | I2C2_SADDR = index; | 370 | ascodec_async_write(&req, index, value); |
196 | I2C2_CNTRL &= ~(1 << 1); | 371 | ascodec_wait(&req); |
197 | I2C2_DATA = value; | 372 | } |
198 | I2C2_DACNT = 1; | ||
199 | 373 | ||
200 | } while (!i2c2_transfer()); | 374 | /* |
375 | * The request struct passed in must be allocated statically. | ||
376 | * If you call ascodec_async_read from different places, each | ||
377 | * call needs it's own request struct. | ||
378 | * If len is bigger than ASCODEC_REQ_MAXLEN it will be | ||
379 | * set to ASCODEC_REQ_MAXLEN. | ||
380 | */ | ||
381 | static void ascodec_async_read(struct ascodec_request *req, | ||
382 | unsigned int index, unsigned int len) | ||
383 | { | ||
384 | if (len > ASCODEC_REQ_MAXLEN) | ||
385 | len = ASCODEC_REQ_MAXLEN; /* can't fit more in one request */ | ||
201 | 386 | ||
202 | ascodec_unlock(); | 387 | ascodec_req_init(req, ASCODEC_REQ_READ, index, len); |
388 | ascodec_submit(req); | ||
203 | } | 389 | } |
204 | 390 | ||
391 | /* read data synchronously */ | ||
205 | int ascodec_read(unsigned int index) | 392 | int ascodec_read(unsigned int index) |
206 | { | 393 | { |
207 | int data; | 394 | struct ascodec_request req; |
395 | struct semaphore complete; | ||
208 | 396 | ||
209 | ascodec_lock(); | 397 | ascodec_wait_init(&req, &complete); |
398 | ascodec_async_read(&req, index, 1); | ||
399 | ascodec_wait(&req); | ||
210 | 400 | ||
211 | do { | 401 | return req.data[0]; |
212 | /* wait if still busy */ | 402 | } |
213 | while (i2c_busy()); | ||
214 | 403 | ||
215 | /* start transfer */ | 404 | /* read an array of bytes */ |
216 | I2C2_SADDR = index; | 405 | void ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data) |
217 | I2C2_CNTRL |= (1 << 1); | 406 | { |
218 | I2C2_DACNT = 1; | 407 | struct ascodec_request req; |
408 | struct semaphore complete; | ||
219 | 409 | ||
220 | } while (!i2c2_transfer()); | 410 | ascodec_wait_init(&req, &complete); |
221 | 411 | ||
222 | data = I2C2_DATA; | 412 | /* index and cnt will be filled in later, just use 0 */ |
413 | ascodec_req_init(&req, ASCODEC_REQ_READ, 0, 0); | ||
223 | 414 | ||
224 | ascodec_unlock(); | 415 | int i = 0; |
225 | 416 | ||
226 | return data; | 417 | while (len > 0) { |
227 | } | 418 | int cnt = MIN(len, ASCODEC_REQ_MAXLEN); |
228 | 419 | ||
229 | void ascodec_readbytes(unsigned int index, unsigned int len, unsigned char *data) | 420 | req.index = index; |
230 | { | 421 | req.cnt = cnt; |
231 | unsigned int i; | 422 | |
423 | ascodec_submit(&req); | ||
424 | ascodec_wait(&req); | ||
425 | |||
426 | for (int j = 0; j < cnt; j++) | ||
427 | data[i++] = req.data[j]; | ||
232 | 428 | ||
233 | for(i = 0; i < len; i++) | 429 | len -= cnt; |
234 | data[i] = ascodec_read(index+i); | 430 | index += cnt; |
431 | } | ||
235 | } | 432 | } |
236 | 433 | ||
237 | #if CONFIG_CPU == AS3525v2 | 434 | #if CONFIG_CPU == AS3525v2 |
435 | /* write special PMU subregisters */ | ||
238 | void ascodec_write_pmu(unsigned int index, unsigned int subreg, | 436 | void ascodec_write_pmu(unsigned int index, unsigned int subreg, |
239 | unsigned int value) | 437 | unsigned int value) |
240 | { | 438 | { |
241 | ascodec_lock(); | 439 | struct ascodec_request reqs[2]; |
440 | struct semaphore complete; | ||
441 | |||
442 | ascodec_async_init(&reqs[0], NULL, 0); | ||
443 | ascodec_wait_init(&reqs[1], &complete); | ||
444 | |||
445 | int oldstatus = disable_irq_save(); | ||
242 | /* we submit consecutive requests to make sure no operations happen on the | 446 | /* we submit consecutive requests to make sure no operations happen on the |
243 | * i2c bus between selecting the sub register and writing to it */ | 447 | * i2c bus between selecting the sub register and writing to it */ |
244 | ascodec_write(AS3543_PMU_ENABLE, 8 | subreg); | 448 | ascodec_async_write(&reqs[0], AS3543_PMU_ENABLE, 8 | subreg); |
245 | ascodec_write(index, value); | 449 | ascodec_async_write(&reqs[1], index, value); |
450 | restore_irq(oldstatus); | ||
246 | 451 | ||
247 | ascodec_unlock(); | 452 | /* Wait for second request to finish */ |
453 | ascodec_wait(&reqs[1]); | ||
248 | } | 454 | } |
249 | 455 | ||
456 | /* read special PMU subregisters */ | ||
250 | int ascodec_read_pmu(unsigned int index, unsigned int subreg) | 457 | int ascodec_read_pmu(unsigned int index, unsigned int subreg) |
251 | { | 458 | { |
252 | ascodec_lock(); | 459 | struct ascodec_request reqs[2]; |
460 | struct semaphore complete; | ||
461 | |||
462 | ascodec_async_init(&reqs[0], NULL, 0); | ||
463 | ascodec_wait_init(&reqs[1], &complete); | ||
464 | |||
465 | int oldstatus = disable_irq_save(); | ||
253 | /* we submit consecutive requests to make sure no operations happen on the | 466 | /* we submit consecutive requests to make sure no operations happen on the |
254 | * i2c bus between selecting the sub register and reading it */ | 467 | * i2c bus between selecting the sub register and reading it */ |
255 | ascodec_write(AS3543_PMU_ENABLE, subreg); | 468 | ascodec_async_write(&reqs[0], AS3543_PMU_ENABLE, subreg); |
256 | int ret = ascodec_read(index); | 469 | ascodec_async_read(&reqs[1], index, 1); |
470 | restore_irq(oldstatus); | ||
257 | 471 | ||
258 | ascodec_unlock(); | 472 | /* Wait for second request to finish */ |
473 | ascodec_wait(&reqs[1]); | ||
259 | 474 | ||
260 | return ret; | 475 | return reqs[1].data[0]; |
261 | } | 476 | } |
262 | #endif /* CONFIG_CPU == AS3525v2 */ | 477 | #endif /* CONFIG_CPU == AS3525v2 */ |
263 | 478 | ||
264 | void INT_AUDIO(void) | 479 | /* callback that receives results of reading INT_AUDIO status register */ |
480 | static void ascodec_int_audio_cb(struct ascodec_request *req) | ||
265 | { | 481 | { |
266 | int oldstatus = disable_irq_save(); | 482 | unsigned char * const data = req->data; |
267 | int data = ascodec_read(AS3514_IRQ_ENRD0); | ||
268 | 483 | ||
269 | #if CONFIG_CHARGING | 484 | if (UNLIKELY(req->len_done != 3)) { /* some error happened? */ |
270 | if (data & CHG_ENDOFCH) { /* chg finished */ | 485 | panicf("INT_AUDIO callback got %d regs", req->len_done); |
271 | endofch = true; | ||
272 | } | 486 | } |
273 | 487 | ||
274 | chg_status = data & CHG_STATUS; | 488 | if (data[0] & CHG_ENDOFCH) { /* chg finished */ |
275 | #endif | 489 | COUNT_INT(chg_finished); |
490 | ascodec_enrd0_shadow |= CHG_ENDOFCH; | ||
491 | } | ||
492 | |||
493 | if (data[0] & CHG_CHANGED) { /* chg status changed */ | ||
494 | if (data[0] & CHG_STATUS) { | ||
495 | COUNT_INT(chg_insert); | ||
496 | ascodec_enrd0_shadow |= CHG_STATUS; | ||
497 | } else { | ||
498 | COUNT_INT(chg_remove); | ||
499 | ascodec_enrd0_shadow &= ~CHG_STATUS; | ||
500 | } | ||
501 | } | ||
276 | 502 | ||
277 | if (data & USB_CHANGED) { /* usb status changed */ | 503 | if (data[0] & USB_CHANGED) { /* usb status changed */ |
278 | if (data & USB_STATUS) { | 504 | if (data[0] & USB_STATUS) { |
505 | COUNT_INT(usb_insert); | ||
279 | usb_insert_int(); | 506 | usb_insert_int(); |
280 | } else { | 507 | } else { |
508 | COUNT_INT(usb_remove); | ||
281 | usb_remove_int(); | 509 | usb_remove_int(); |
282 | } | 510 | } |
283 | } | 511 | } |
284 | 512 | ||
285 | restore_irq(oldstatus); | 513 | if (data[2] & IRQ_RTC) { /* rtc irq */ |
514 | /* | ||
515 | * Can be configured for once per second or once per minute, | ||
516 | * default is once per second | ||
517 | */ | ||
518 | COUNT_INT(rtc); | ||
519 | } | ||
520 | |||
521 | if (data[2] & IRQ_ADC) { /* adc finished */ | ||
522 | COUNT_INT(adc); | ||
523 | semaphore_release(&adc_done_sem); | ||
524 | } | ||
525 | |||
526 | VIC_INT_ENABLE = INTERRUPT_AUDIO; | ||
527 | } | ||
528 | |||
529 | /* ISR for all various ascodec events */ | ||
530 | void INT_AUDIO(void) | ||
531 | { | ||
532 | VIC_INT_EN_CLEAR = INTERRUPT_AUDIO; | ||
533 | COUNT_INT(audio); | ||
534 | |||
535 | ascodec_async_read(&as_audio_req, AS3514_IRQ_ENRD0, 3); | ||
536 | } | ||
537 | |||
538 | /* wait for ADC to finish conversion */ | ||
539 | void ascodec_wait_adc_finished(void) | ||
540 | { | ||
541 | semaphore_wait(&adc_done_sem, TIMEOUT_BLOCK); | ||
286 | } | 542 | } |
287 | 543 | ||
288 | #if CONFIG_CHARGING | 544 | #if CONFIG_CHARGING |
545 | /* read sticky end-of-charge bit and clear it */ | ||
289 | bool ascodec_endofch(void) | 546 | bool ascodec_endofch(void) |
290 | { | 547 | { |
291 | int oldstatus = disable_irq_save(); | 548 | int oldlevel = disable_irq_save(); |
292 | bool ret = endofch; | 549 | |
293 | endofch = false; | 550 | bool ret = ascodec_enrd0_shadow & CHG_ENDOFCH; |
294 | restore_irq(oldstatus); | 551 | ascodec_enrd0_shadow &= ~CHG_ENDOFCH; /* clear interrupt */ |
552 | |||
553 | restore_irq(oldlevel); | ||
295 | 554 | ||
296 | return ret; | 555 | return ret; |
297 | } | 556 | } |
298 | 557 | ||
558 | /* read the presence state of the charger */ | ||
299 | bool ascodec_chg_status(void) | 559 | bool ascodec_chg_status(void) |
300 | { | 560 | { |
301 | return chg_status; | 561 | return ascodec_enrd0_shadow & CHG_STATUS; |
302 | } | 562 | } |
303 | 563 | ||
304 | void ascodec_monitor_endofch(void) | 564 | void ascodec_monitor_endofch(void) |
305 | { | 565 | { |
306 | /* already enabled */ | 566 | /* end of charge status interrupt already enabled */ |
307 | } | 567 | } |
308 | 568 | ||
569 | /* write charger control register */ | ||
309 | void ascodec_write_charger(int value) | 570 | void ascodec_write_charger(int value) |
310 | { | 571 | { |
311 | #if CONFIG_CPU == AS3525 | 572 | #if CONFIG_CPU == AS3525 |
@@ -315,6 +576,7 @@ void ascodec_write_charger(int value) | |||
315 | #endif | 576 | #endif |
316 | } | 577 | } |
317 | 578 | ||
579 | /* read charger control register */ | ||
318 | int ascodec_read_charger(void) | 580 | int ascodec_read_charger(void) |
319 | { | 581 | { |
320 | #if CONFIG_CPU == AS3525 | 582 | #if CONFIG_CPU == AS3525 |
@@ -325,6 +587,15 @@ int ascodec_read_charger(void) | |||
325 | } | 587 | } |
326 | #endif /* CONFIG_CHARGING */ | 588 | #endif /* CONFIG_CHARGING */ |
327 | 589 | ||
590 | /* | ||
591 | * NOTE: | ||
592 | * After the conversion to interrupts, ascodec_(lock|unlock) are only used by | ||
593 | * adc-as3514.c to protect against other threads corrupting the result by using | ||
594 | * the ADC at the same time. | ||
595 | * Concurrent ascodec_(async_)?(read|write) calls are instead protected | ||
596 | * because ascodec_submit() is atomic and concurrent requests will wait | ||
597 | * in the queue until the current request is finished. | ||
598 | */ | ||
328 | void ascodec_lock(void) | 599 | void ascodec_lock(void) |
329 | { | 600 | { |
330 | mutex_lock(&as_mtx); | 601 | mutex_lock(&as_mtx); |
@@ -334,3 +605,63 @@ void ascodec_unlock(void) | |||
334 | { | 605 | { |
335 | mutex_unlock(&as_mtx); | 606 | mutex_unlock(&as_mtx); |
336 | } | 607 | } |
608 | |||
609 | |||
610 | /** --Startup initialization-- **/ | ||
611 | |||
612 | void i2c_init(void) | ||
613 | { | ||
614 | /* required function but called too late for our needs */ | ||
615 | } | ||
616 | |||
617 | /* initialises the internal i2c bus and prepares for transfers to the codec */ | ||
618 | void ascodec_init(void) | ||
619 | { | ||
620 | int prescaler; | ||
621 | |||
622 | ll_init(&req_list); | ||
623 | mutex_init(&as_mtx); | ||
624 | ascodec_async_init(&as_audio_req, ascodec_int_audio_cb, 0); | ||
625 | semaphore_init(&adc_done_sem, 1, 0); | ||
626 | |||
627 | /* enable clock */ | ||
628 | bitset32(&CGU_PERI, CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE); | ||
629 | |||
630 | /* prescaler for i2c clock */ | ||
631 | prescaler = AS3525_I2C_PRESCALER; | ||
632 | I2C2_CPSR0 = prescaler & 0xFF; /* 8 lsb */ | ||
633 | I2C2_CPSR1 = (prescaler >> 8) & 0x3; /* 2 msb */ | ||
634 | |||
635 | /* set i2c slave address of codec part */ | ||
636 | I2C2_SLAD0 = AS3514_I2C_ADDR << 1; | ||
637 | |||
638 | I2C2_CNTRL = I2C2_CNTRL_DEFAULT; | ||
639 | |||
640 | I2C2_IMR = 0x00; /* disable interrupts */ | ||
641 | I2C2_INT_CLR = I2C2_RIS; /* clear interrupt status */ | ||
642 | VIC_INT_ENABLE = INTERRUPT_I2C_AUDIO; | ||
643 | VIC_INT_ENABLE = INTERRUPT_AUDIO; | ||
644 | |||
645 | /* detect if USB was connected at startup since there is no transition */ | ||
646 | ascodec_enrd0_shadow = ascodec_read(AS3514_IRQ_ENRD0); | ||
647 | if(ascodec_enrd0_shadow & USB_STATUS) | ||
648 | usb_insert_int(); | ||
649 | |||
650 | /* Generate irq for usb+charge status change */ | ||
651 | ascodec_write(AS3514_IRQ_ENRD0, | ||
652 | #if CONFIG_CHARGING /* m200v4 can't charge */ | ||
653 | IRQ_CHGSTAT | IRQ_ENDOFCH | | ||
654 | #endif | ||
655 | IRQ_USBSTAT); | ||
656 | |||
657 | #if CONFIG_CPU == AS3525v2 | ||
658 | /* XIRQ = IRQ, active low reset signal, 6mA push-pull output */ | ||
659 | ascodec_write_pmu(0x1a, 3, (1<<2)|3); /* 1A-3 = Out_Cntr3 register */ | ||
660 | /* Generate irq on (rtc,) adc change */ | ||
661 | ascodec_write(AS3514_IRQ_ENRD2, /*IRQ_RTC |*/ IRQ_ADC); | ||
662 | #else | ||
663 | /* Generate irq for push-pull, active high, irq on rtc+adc change */ | ||
664 | ascodec_write(AS3514_IRQ_ENRD2, IRQ_PUSHPULL | IRQ_HIGHACTIVE | | ||
665 | /*IRQ_RTC |*/ IRQ_ADC); | ||
666 | #endif | ||
667 | } | ||
diff --git a/firmware/target/arm/as3525/debug-as3525.c b/firmware/target/arm/as3525/debug-as3525.c index 1ee4de64cc..a1e69834dd 100644 --- a/firmware/target/arm/as3525/debug-as3525.c +++ b/firmware/target/arm/as3525/debug-as3525.c | |||
@@ -605,3 +605,33 @@ end: | |||
605 | lcd_setfont(FONT_UI); | 605 | lcd_setfont(FONT_UI); |
606 | return false; | 606 | return false; |
607 | } | 607 | } |
608 | |||
609 | #ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE | ||
610 | /* Return CPU voltage setting in millivolts */ | ||
611 | int get_cpu_voltage_setting(void) | ||
612 | { | ||
613 | int value; | ||
614 | |||
615 | #if CONFIG_CPU == AS3525 | ||
616 | value = ascodec_read(AS3514_CVDD_DCDC3) & 0x3; | ||
617 | value = 1200 - value * 50; | ||
618 | #else /* as3525v2 */ | ||
619 | value = ascodec_read_pmu(0x17, 1) & 0x7f; | ||
620 | |||
621 | /* Calculate in 0.1mV steps */ | ||
622 | if (value == 0) | ||
623 | /* 0 volts */; | ||
624 | else if (value <= 0x40) | ||
625 | value = 6000 + value * 125; | ||
626 | else if (value <= 0x70) | ||
627 | value = 14000 + (value - 0x40) * 250; | ||
628 | else if (value <= 0x7f) | ||
629 | value = 26000 + (value - 0x70) * 500; | ||
630 | |||
631 | /* Return voltage setting in millivolts */ | ||
632 | value = (value + 5) / 10; | ||
633 | #endif /* CONFIG_CPU */ | ||
634 | |||
635 | return value; | ||
636 | } | ||
637 | #endif /* HAVE_ADJUSTABLE_CPU_VOLTAGE */ | ||
diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c index 8aa2d02ab7..0ea0c8fba4 100644 --- a/firmware/target/arm/as3525/system-as3525.c +++ b/firmware/target/arm/as3525/system-as3525.c | |||
@@ -33,6 +33,8 @@ | |||
33 | #include "backlight-target.h" | 33 | #include "backlight-target.h" |
34 | #include "lcd.h" | 34 | #include "lcd.h" |
35 | 35 | ||
36 | struct mutex cpufreq_mtx; | ||
37 | |||
36 | /* Charge Pump and Power management Settings */ | 38 | /* Charge Pump and Power management Settings */ |
37 | #define AS314_CP_DCDC3_SETTING \ | 39 | #define AS314_CP_DCDC3_SETTING \ |
38 | ((0<<7) | /* CP_SW Auto-Switch Margin 0=200/300 1=150/255 */ \ | 40 | ((0<<7) | /* CP_SW Auto-Switch Margin 0=200/300 1=150/255 */ \ |
@@ -144,6 +146,7 @@ static const struct { int source; void (*isr) (void); } vec_int_srcs[] = | |||
144 | { INT_SRC_USB, INT_USB_FUNC, }, | 146 | { INT_SRC_USB, INT_USB_FUNC, }, |
145 | { INT_SRC_TIMER1, INT_TIMER1 }, | 147 | { INT_SRC_TIMER1, INT_TIMER1 }, |
146 | { INT_SRC_TIMER2, INT_TIMER2 }, | 148 | { INT_SRC_TIMER2, INT_TIMER2 }, |
149 | { INT_SRC_I2C_AUDIO, INT_I2C_AUDIO }, | ||
147 | { INT_SRC_AUDIO, INT_AUDIO }, | 150 | { INT_SRC_AUDIO, INT_AUDIO }, |
148 | /* Lowest priority at the end of the list */ | 151 | /* Lowest priority at the end of the list */ |
149 | }; | 152 | }; |
@@ -322,6 +325,12 @@ void system_init(void) | |||
322 | setup_vic(); | 325 | setup_vic(); |
323 | 326 | ||
324 | dma_init(); | 327 | dma_init(); |
328 | } | ||
329 | |||
330 | /* this is called after kernel and threading are initialized */ | ||
331 | void kernel_device_init(void) | ||
332 | { | ||
333 | mutex_init(&cpufreq_mtx); | ||
325 | 334 | ||
326 | ascodec_init(); | 335 | ascodec_init(); |
327 | 336 | ||
@@ -329,7 +338,8 @@ void system_init(void) | |||
329 | #ifdef HAVE_AS3543 | 338 | #ifdef HAVE_AS3543 |
330 | /* PLL: disable audio PLL, we use MCLK already */ | 339 | /* PLL: disable audio PLL, we use MCLK already */ |
331 | ascodec_write_pmu(0x1A, 7, 0x02); | 340 | ascodec_write_pmu(0x1A, 7, 0x02); |
332 | /* DCDC_Cntr: set switching speed of CVDD1/2 power supplies to 1 MHz */ | 341 | /* DCDC_Cntr: set switching speed of CVDD1/2 power supplies to 1 MHz, |
342 | immediate change */ | ||
333 | ascodec_write_pmu(0x17, 7, 0x30); | 343 | ascodec_write_pmu(0x17, 7, 0x30); |
334 | /* Out_Cntr2: set drive strength of 24 MHz and 32 kHz clocks to 1 mA */ | 344 | /* Out_Cntr2: set drive strength of 24 MHz and 32 kHz clocks to 1 mA */ |
335 | ascodec_write_pmu(0x1A, 2, 0xCC); | 345 | ascodec_write_pmu(0x1A, 2, 0xCC); |
@@ -414,11 +424,28 @@ void udelay(unsigned usecs) | |||
414 | 424 | ||
415 | #ifndef BOOTLOADER | 425 | #ifndef BOOTLOADER |
416 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 426 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
427 | bool set_cpu_frequency__lock(void) | ||
428 | { | ||
429 | if (get_processor_mode() != CPU_MODE_THREAD_CONTEXT) | ||
430 | return false; | ||
431 | |||
432 | mutex_lock(&cpufreq_mtx); | ||
433 | return true; | ||
434 | } | ||
435 | |||
436 | void set_cpu_frequency__unlock(void) | ||
437 | { | ||
438 | mutex_unlock(&cpufreq_mtx); | ||
439 | } | ||
417 | 440 | ||
418 | #if CONFIG_CPU == AS3525 | 441 | #if CONFIG_CPU == AS3525 |
419 | void set_cpu_frequency(long frequency) | 442 | void set_cpu_frequency(long frequency) |
420 | { | 443 | { |
421 | if(frequency == CPUFREQ_MAX) | 444 | if (frequency == cpu_frequency) |
445 | { | ||
446 | /* avoid redundant activity */ | ||
447 | } | ||
448 | else if(frequency == CPUFREQ_MAX) | ||
422 | { | 449 | { |
423 | #ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE | 450 | #ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE |
424 | /* Increasing frequency so boost voltage before change */ | 451 | /* Increasing frequency so boost voltage before change */ |
@@ -464,7 +491,11 @@ void set_cpu_frequency(long frequency) | |||
464 | #else /* as3525v2 */ | 491 | #else /* as3525v2 */ |
465 | void set_cpu_frequency(long frequency) | 492 | void set_cpu_frequency(long frequency) |
466 | { | 493 | { |
467 | if(frequency == CPUFREQ_MAX) | 494 | if (frequency == cpu_frequency) |
495 | { | ||
496 | /* avoid redundant activity */ | ||
497 | } | ||
498 | else if(frequency == CPUFREQ_MAX) | ||
468 | { | 499 | { |
469 | #ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE | 500 | #ifdef HAVE_ADJUSTABLE_CPU_VOLTAGE |
470 | /* Set CVDD1 power supply */ | 501 | /* Set CVDD1 power supply */ |
diff --git a/firmware/target/arm/as3525/system-target.h b/firmware/target/arm/as3525/system-target.h index db5bb892ef..4fbbb46d5d 100644 --- a/firmware/target/arm/as3525/system-target.h +++ b/firmware/target/arm/as3525/system-target.h | |||
@@ -21,12 +21,17 @@ | |||
21 | #ifndef SYSTEM_TARGET_H | 21 | #ifndef SYSTEM_TARGET_H |
22 | #define SYSTEM_TARGET_H | 22 | #define SYSTEM_TARGET_H |
23 | 23 | ||
24 | /* we need some system things initialized after the kernel init */ | ||
25 | #define KDEV_INIT | ||
26 | |||
24 | #include "system-arm.h" | 27 | #include "system-arm.h" |
25 | #include "mmu-arm.h" | 28 | #include "mmu-arm.h" |
26 | #include "panic.h" | 29 | #include "panic.h" |
27 | 30 | ||
28 | #include "clock-target.h" /* CPUFREQ_* are defined here */ | 31 | #include "clock-target.h" /* CPUFREQ_* are defined here */ |
29 | 32 | ||
33 | void kernel_device_init(void); | ||
34 | |||
30 | #define STORAGE_WANTS_ALIGN | 35 | #define STORAGE_WANTS_ALIGN |
31 | 36 | ||
32 | /* We can use a interrupt-based mechanism on the fuzev2 */ | 37 | /* We can use a interrupt-based mechanism on the fuzev2 */ |
@@ -68,4 +73,20 @@ static inline void mdelay(unsigned msecs) | |||
68 | void usb_insert_int(void); | 73 | void usb_insert_int(void); |
69 | void usb_remove_int(void); | 74 | void usb_remove_int(void); |
70 | 75 | ||
76 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | ||
77 | #define CPU_BOOST_LOCK_DEFINED | ||
78 | |||
79 | static inline bool cpu_boost_lock(void) | ||
80 | { | ||
81 | bool set_cpu_frequency__lock(void); | ||
82 | return set_cpu_frequency__lock(); | ||
83 | } | ||
84 | |||
85 | static inline void cpu_boost_unlock(void) | ||
86 | { | ||
87 | void set_cpu_frequency__unlock(void); | ||
88 | set_cpu_frequency__unlock(); | ||
89 | } | ||
90 | #endif /* HAVE_ADJUSTABLE_CPU_FREQ */ | ||
91 | |||
71 | #endif /* SYSTEM_TARGET_H */ | 92 | #endif /* SYSTEM_TARGET_H */ |
diff --git a/firmware/target/arm/pp/system-pp5002.c b/firmware/target/arm/pp/system-pp5002.c index 3186d3739a..388f962fce 100644 --- a/firmware/target/arm/pp/system-pp5002.c +++ b/firmware/target/arm/pp/system-pp5002.c | |||
@@ -24,6 +24,11 @@ | |||
24 | #include "adc-target.h" | 24 | #include "adc-target.h" |
25 | #include "button-target.h" | 25 | #include "button-target.h" |
26 | 26 | ||
27 | #if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1) | ||
28 | #include "corelock.h" | ||
29 | static struct corelock cpufreq_cl SHAREDBSS_ATTR; | ||
30 | #endif | ||
31 | |||
27 | extern void TIMER1(void); | 32 | extern void TIMER1(void); |
28 | extern void TIMER2(void); | 33 | extern void TIMER2(void); |
29 | 34 | ||
@@ -122,6 +127,18 @@ static void ipod_init_cache(void) | |||
122 | } | 127 | } |
123 | 128 | ||
124 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 129 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
130 | #if NUM_CORES > 1 | ||
131 | void set_cpu_frequency__lock(void) | ||
132 | { | ||
133 | corelock_lock(&cpufreq_cl); | ||
134 | } | ||
135 | |||
136 | void set_cpu_frequency__unlock(void) | ||
137 | { | ||
138 | corelock_unlock(&cpufreq_cl); | ||
139 | } | ||
140 | #endif /* NUM_CORES > 1 */ | ||
141 | |||
125 | void set_cpu_frequency(long frequency) | 142 | void set_cpu_frequency(long frequency) |
126 | #else | 143 | #else |
127 | static void pp_set_cpu_frequency(long frequency) | 144 | static void pp_set_cpu_frequency(long frequency) |
@@ -193,7 +210,7 @@ void system_init(void) | |||
193 | 210 | ||
194 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 211 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
195 | #if NUM_CORES > 1 | 212 | #if NUM_CORES > 1 |
196 | cpu_boost_init(); | 213 | corelock_init(&cpufreq_cl); |
197 | #endif | 214 | #endif |
198 | #else | 215 | #else |
199 | pp_set_cpu_frequency(CPUFREQ_MAX); | 216 | pp_set_cpu_frequency(CPUFREQ_MAX); |
diff --git a/firmware/target/arm/pp/system-pp502x.c b/firmware/target/arm/pp/system-pp502x.c index 99b536e132..102cfd8fea 100644 --- a/firmware/target/arm/pp/system-pp502x.c +++ b/firmware/target/arm/pp/system-pp502x.c | |||
@@ -308,16 +308,24 @@ void scale_suspend_core(bool suspend) | |||
308 | } | 308 | } |
309 | 309 | ||
310 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 310 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
311 | #if NUM_CORES > 1 | ||
312 | void set_cpu_frequency__lock(void) | ||
313 | { | ||
314 | corelock_lock(&cpufreq_cl); | ||
315 | } | ||
316 | |||
317 | void set_cpu_frequency__unlock(void) | ||
318 | { | ||
319 | corelock_unlock(&cpufreq_cl); | ||
320 | } | ||
321 | #endif /* NUM_CORES > 1 */ | ||
322 | |||
311 | void set_cpu_frequency(long frequency) ICODE_ATTR; | 323 | void set_cpu_frequency(long frequency) ICODE_ATTR; |
312 | void set_cpu_frequency(long frequency) | 324 | void set_cpu_frequency(long frequency) |
313 | #else | 325 | #else |
314 | static void pp_set_cpu_frequency(long frequency) | 326 | static void pp_set_cpu_frequency(long frequency) |
315 | #endif | 327 | #endif |
316 | { | 328 | { |
317 | #if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1) | ||
318 | corelock_lock(&cpufreq_cl); | ||
319 | #endif | ||
320 | |||
321 | switch (frequency) | 329 | switch (frequency) |
322 | { | 330 | { |
323 | /* Note1: The PP5022 PLL must be run at >= 96MHz | 331 | /* Note1: The PP5022 PLL must be run at >= 96MHz |
@@ -424,10 +432,6 @@ static void pp_set_cpu_frequency(long frequency) | |||
424 | DEV_INIT2 &= ~INIT_PLL; /* disable PLL power */ | 432 | DEV_INIT2 &= ~INIT_PLL; /* disable PLL power */ |
425 | break; | 433 | break; |
426 | } | 434 | } |
427 | |||
428 | #if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1) | ||
429 | corelock_unlock(&cpufreq_cl); | ||
430 | #endif | ||
431 | } | 435 | } |
432 | #endif /* !BOOTLOADER || (SANSA_E200 || SANSA_C200 || PHILIPS_SA9200) */ | 436 | #endif /* !BOOTLOADER || (SANSA_E200 || SANSA_C200 || PHILIPS_SA9200) */ |
433 | 437 | ||
@@ -544,7 +548,6 @@ void system_init(void) | |||
544 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 548 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
545 | #if NUM_CORES > 1 | 549 | #if NUM_CORES > 1 |
546 | corelock_init(&cpufreq_cl); | 550 | corelock_init(&cpufreq_cl); |
547 | cpu_boost_init(); | ||
548 | #endif | 551 | #endif |
549 | #else | 552 | #else |
550 | pp_set_cpu_frequency(CPUFREQ_MAX); | 553 | pp_set_cpu_frequency(CPUFREQ_MAX); |
diff --git a/firmware/target/arm/pp/system-target.h b/firmware/target/arm/pp/system-target.h index d372b65502..1e947195bd 100644 --- a/firmware/target/arm/pp/system-target.h +++ b/firmware/target/arm/pp/system-target.h | |||
@@ -199,4 +199,21 @@ void system_prepare_fw_start(void); | |||
199 | 199 | ||
200 | #endif /* BOOTLOADER */ | 200 | #endif /* BOOTLOADER */ |
201 | 201 | ||
202 | #if defined(HAVE_ADJUSTABLE_CPU_FREQ) && (NUM_CORES > 1) | ||
203 | #define CPU_BOOST_LOCK_DEFINED | ||
204 | |||
205 | static inline bool cpu_boost_lock(void) | ||
206 | { | ||
207 | void set_cpu_frequency__lock(void); | ||
208 | set_cpu_frequency__lock(); | ||
209 | return true; | ||
210 | } | ||
211 | |||
212 | static inline void cpu_boost_unlock(void) | ||
213 | { | ||
214 | void set_cpu_frequency__unlock(void); | ||
215 | set_cpu_frequency__unlock(); | ||
216 | } | ||
217 | #endif /* HAVE_ADJUSTABLE_CPU_FREQ && NUM_CORES > 1 */ | ||
218 | |||
202 | #endif /* SYSTEM_TARGET_H */ | 219 | #endif /* SYSTEM_TARGET_H */ |