diff options
Diffstat (limited to 'arch/arm/kernel')
| -rw-r--r-- | arch/arm/kernel/Makefile | 4 | ||||
| -rw-r--r-- | arch/arm/kernel/apm.c | 187 | ||||
| -rw-r--r-- | arch/arm/kernel/asm-offsets.c | 1 | ||||
| -rw-r--r-- | arch/arm/kernel/ecard.c | 10 | ||||
| -rw-r--r-- | arch/arm/kernel/entry-armv.S | 9 | ||||
| -rw-r--r-- | arch/arm/kernel/head-nommu.S | 1 | ||||
| -rw-r--r-- | arch/arm/kernel/head.S | 1 | ||||
| -rw-r--r-- | arch/arm/kernel/irq.c | 8 | ||||
| -rw-r--r-- | arch/arm/kernel/iwmmxt-notifier.c | 63 | ||||
| -rw-r--r-- | arch/arm/kernel/process.c | 61 | ||||
| -rw-r--r-- | arch/arm/kernel/setup.c | 14 | ||||
| -rw-r--r-- | arch/arm/kernel/signal.c | 1 | ||||
| -rw-r--r-- | arch/arm/kernel/traps.c | 7 | ||||
| -rw-r--r-- | arch/arm/kernel/xscale-cp0.c | 179 |
14 files changed, 333 insertions, 213 deletions
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index 1320a0efca73..ab06a86e85d5 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile | |||
| @@ -24,7 +24,9 @@ obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o | |||
| 24 | obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o | 24 | obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o |
| 25 | AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 | 25 | AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 |
| 26 | 26 | ||
| 27 | obj-$(CONFIG_IWMMXT) += iwmmxt.o iwmmxt-notifier.o | 27 | obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o |
| 28 | obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o | ||
| 29 | obj-$(CONFIG_IWMMXT) += iwmmxt.o | ||
| 28 | AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt | 30 | AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt |
| 29 | 31 | ||
| 30 | ifneq ($(CONFIG_ARCH_EBSA110),y) | 32 | ifneq ($(CONFIG_ARCH_EBSA110),y) |
diff --git a/arch/arm/kernel/apm.c b/arch/arm/kernel/apm.c index ecf4f9472d94..a11fb9a40c04 100644 --- a/arch/arm/kernel/apm.c +++ b/arch/arm/kernel/apm.c | |||
| @@ -12,7 +12,6 @@ | |||
| 12 | */ | 12 | */ |
| 13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
| 14 | #include <linux/poll.h> | 14 | #include <linux/poll.h> |
| 15 | #include <linux/timer.h> | ||
| 16 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
| 17 | #include <linux/proc_fs.h> | 16 | #include <linux/proc_fs.h> |
| 18 | #include <linux/miscdevice.h> | 17 | #include <linux/miscdevice.h> |
| @@ -26,6 +25,7 @@ | |||
| 26 | #include <linux/init.h> | 25 | #include <linux/init.h> |
| 27 | #include <linux/completion.h> | 26 | #include <linux/completion.h> |
| 28 | #include <linux/kthread.h> | 27 | #include <linux/kthread.h> |
| 28 | #include <linux/delay.h> | ||
| 29 | 29 | ||
| 30 | #include <asm/apm.h> /* apm_power_info */ | 30 | #include <asm/apm.h> /* apm_power_info */ |
| 31 | #include <asm/system.h> | 31 | #include <asm/system.h> |
| @@ -71,7 +71,8 @@ struct apm_user { | |||
| 71 | #define SUSPEND_PENDING 1 /* suspend pending read */ | 71 | #define SUSPEND_PENDING 1 /* suspend pending read */ |
| 72 | #define SUSPEND_READ 2 /* suspend read, pending ack */ | 72 | #define SUSPEND_READ 2 /* suspend read, pending ack */ |
| 73 | #define SUSPEND_ACKED 3 /* suspend acked */ | 73 | #define SUSPEND_ACKED 3 /* suspend acked */ |
| 74 | #define SUSPEND_DONE 4 /* suspend completed */ | 74 | #define SUSPEND_WAIT 4 /* waiting for suspend */ |
| 75 | #define SUSPEND_DONE 5 /* suspend completed */ | ||
| 75 | 76 | ||
| 76 | struct apm_queue queue; | 77 | struct apm_queue queue; |
| 77 | }; | 78 | }; |
| @@ -101,6 +102,7 @@ static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait); | |||
| 101 | static DEFINE_SPINLOCK(kapmd_queue_lock); | 102 | static DEFINE_SPINLOCK(kapmd_queue_lock); |
| 102 | static struct apm_queue kapmd_queue; | 103 | static struct apm_queue kapmd_queue; |
| 103 | 104 | ||
| 105 | static DEFINE_MUTEX(state_lock); | ||
| 104 | 106 | ||
| 105 | static const char driver_version[] = "1.13"; /* no spaces */ | 107 | static const char driver_version[] = "1.13"; /* no spaces */ |
| 106 | 108 | ||
| @@ -148,38 +150,60 @@ static void queue_add_event(struct apm_queue *q, apm_event_t event) | |||
| 148 | q->events[q->event_head] = event; | 150 | q->events[q->event_head] = event; |
| 149 | } | 151 | } |
| 150 | 152 | ||
| 151 | static void queue_event_one_user(struct apm_user *as, apm_event_t event) | 153 | static void queue_event(apm_event_t event) |
| 152 | { | 154 | { |
| 153 | if (as->suser && as->writer) { | 155 | struct apm_user *as; |
| 154 | switch (event) { | ||
| 155 | case APM_SYS_SUSPEND: | ||
| 156 | case APM_USER_SUSPEND: | ||
| 157 | /* | ||
| 158 | * If this user already has a suspend pending, | ||
| 159 | * don't queue another one. | ||
| 160 | */ | ||
| 161 | if (as->suspend_state != SUSPEND_NONE) | ||
| 162 | return; | ||
| 163 | 156 | ||
| 164 | as->suspend_state = SUSPEND_PENDING; | 157 | down_read(&user_list_lock); |
| 165 | suspends_pending++; | 158 | list_for_each_entry(as, &apm_user_list, list) { |
| 166 | break; | 159 | if (as->reader) |
| 167 | } | 160 | queue_add_event(&as->queue, event); |
| 168 | } | 161 | } |
| 169 | queue_add_event(&as->queue, event); | 162 | up_read(&user_list_lock); |
| 163 | wake_up_interruptible(&apm_waitqueue); | ||
| 170 | } | 164 | } |
| 171 | 165 | ||
| 172 | static void queue_event(apm_event_t event, struct apm_user *sender) | 166 | /* |
| 167 | * queue_suspend_event - queue an APM suspend event. | ||
| 168 | * | ||
| 169 | * Check that we're in a state where we can suspend. If not, | ||
| 170 | * return -EBUSY. Otherwise, queue an event to all "writer" | ||
| 171 | * users. If there are no "writer" users, return '1' to | ||
| 172 | * indicate that we can immediately suspend. | ||
| 173 | */ | ||
| 174 | static int queue_suspend_event(apm_event_t event, struct apm_user *sender) | ||
| 173 | { | 175 | { |
| 174 | struct apm_user *as; | 176 | struct apm_user *as; |
| 177 | int ret = 1; | ||
| 175 | 178 | ||
| 179 | mutex_lock(&state_lock); | ||
| 176 | down_read(&user_list_lock); | 180 | down_read(&user_list_lock); |
| 181 | |||
| 182 | /* | ||
| 183 | * If a thread is still processing, we can't suspend, so reject | ||
| 184 | * the request. | ||
| 185 | */ | ||
| 177 | list_for_each_entry(as, &apm_user_list, list) { | 186 | list_for_each_entry(as, &apm_user_list, list) { |
| 178 | if (as != sender && as->reader) | 187 | if (as != sender && as->reader && as->writer && as->suser && |
| 179 | queue_event_one_user(as, event); | 188 | as->suspend_state != SUSPEND_NONE) { |
| 189 | ret = -EBUSY; | ||
| 190 | goto out; | ||
| 191 | } | ||
| 180 | } | 192 | } |
| 193 | |||
| 194 | list_for_each_entry(as, &apm_user_list, list) { | ||
| 195 | if (as != sender && as->reader && as->writer && as->suser) { | ||
| 196 | as->suspend_state = SUSPEND_PENDING; | ||
| 197 | suspends_pending++; | ||
| 198 | queue_add_event(&as->queue, event); | ||
| 199 | ret = 0; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | out: | ||
| 181 | up_read(&user_list_lock); | 203 | up_read(&user_list_lock); |
| 204 | mutex_unlock(&state_lock); | ||
| 182 | wake_up_interruptible(&apm_waitqueue); | 205 | wake_up_interruptible(&apm_waitqueue); |
| 206 | return ret; | ||
| 183 | } | 207 | } |
| 184 | 208 | ||
| 185 | static void apm_suspend(void) | 209 | static void apm_suspend(void) |
| @@ -191,17 +215,22 @@ static void apm_suspend(void) | |||
| 191 | * Anyone on the APM queues will think we're still suspended. | 215 | * Anyone on the APM queues will think we're still suspended. |
| 192 | * Send a message so everyone knows we're now awake again. | 216 | * Send a message so everyone knows we're now awake again. |
| 193 | */ | 217 | */ |
| 194 | queue_event(APM_NORMAL_RESUME, NULL); | 218 | queue_event(APM_NORMAL_RESUME); |
| 195 | 219 | ||
| 196 | /* | 220 | /* |
| 197 | * Finally, wake up anyone who is sleeping on the suspend. | 221 | * Finally, wake up anyone who is sleeping on the suspend. |
| 198 | */ | 222 | */ |
| 223 | mutex_lock(&state_lock); | ||
| 199 | down_read(&user_list_lock); | 224 | down_read(&user_list_lock); |
| 200 | list_for_each_entry(as, &apm_user_list, list) { | 225 | list_for_each_entry(as, &apm_user_list, list) { |
| 201 | as->suspend_result = err; | 226 | if (as->suspend_state == SUSPEND_WAIT || |
| 202 | as->suspend_state = SUSPEND_DONE; | 227 | as->suspend_state == SUSPEND_ACKED) { |
| 228 | as->suspend_result = err; | ||
| 229 | as->suspend_state = SUSPEND_DONE; | ||
| 230 | } | ||
| 203 | } | 231 | } |
| 204 | up_read(&user_list_lock); | 232 | up_read(&user_list_lock); |
| 233 | mutex_unlock(&state_lock); | ||
| 205 | 234 | ||
| 206 | wake_up(&apm_suspend_waitqueue); | 235 | wake_up(&apm_suspend_waitqueue); |
| 207 | } | 236 | } |
| @@ -227,8 +256,11 @@ static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t | |||
| 227 | if (copy_to_user(buf, &event, sizeof(event))) | 256 | if (copy_to_user(buf, &event, sizeof(event))) |
| 228 | break; | 257 | break; |
| 229 | 258 | ||
| 230 | if (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND) | 259 | mutex_lock(&state_lock); |
| 260 | if (as->suspend_state == SUSPEND_PENDING && | ||
| 261 | (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND)) | ||
| 231 | as->suspend_state = SUSPEND_READ; | 262 | as->suspend_state = SUSPEND_READ; |
| 263 | mutex_unlock(&state_lock); | ||
| 232 | 264 | ||
| 233 | buf += sizeof(event); | 265 | buf += sizeof(event); |
| 234 | i -= sizeof(event); | 266 | i -= sizeof(event); |
| @@ -270,9 +302,13 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) | |||
| 270 | 302 | ||
| 271 | switch (cmd) { | 303 | switch (cmd) { |
| 272 | case APM_IOC_SUSPEND: | 304 | case APM_IOC_SUSPEND: |
| 305 | mutex_lock(&state_lock); | ||
| 306 | |||
| 273 | as->suspend_result = -EINTR; | 307 | as->suspend_result = -EINTR; |
| 274 | 308 | ||
| 275 | if (as->suspend_state == SUSPEND_READ) { | 309 | if (as->suspend_state == SUSPEND_READ) { |
| 310 | int pending; | ||
| 311 | |||
| 276 | /* | 312 | /* |
| 277 | * If we read a suspend command from /dev/apm_bios, | 313 | * If we read a suspend command from /dev/apm_bios, |
| 278 | * then the corresponding APM_IOC_SUSPEND ioctl is | 314 | * then the corresponding APM_IOC_SUSPEND ioctl is |
| @@ -280,47 +316,73 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) | |||
| 280 | */ | 316 | */ |
| 281 | as->suspend_state = SUSPEND_ACKED; | 317 | as->suspend_state = SUSPEND_ACKED; |
| 282 | suspends_pending--; | 318 | suspends_pending--; |
| 319 | pending = suspends_pending == 0; | ||
| 320 | mutex_unlock(&state_lock); | ||
| 321 | |||
| 322 | /* | ||
| 323 | * If there are no further acknowledges required, | ||
| 324 | * suspend the system. | ||
| 325 | */ | ||
| 326 | if (pending) | ||
| 327 | apm_suspend(); | ||
| 328 | |||
| 329 | /* | ||
| 330 | * Wait for the suspend/resume to complete. If there | ||
| 331 | * are pending acknowledges, we wait here for them. | ||
| 332 | * | ||
| 333 | * Note: we need to ensure that the PM subsystem does | ||
| 334 | * not kick us out of the wait when it suspends the | ||
| 335 | * threads. | ||
| 336 | */ | ||
| 337 | flags = current->flags; | ||
| 338 | current->flags |= PF_NOFREEZE; | ||
| 339 | |||
| 340 | wait_event(apm_suspend_waitqueue, | ||
| 341 | as->suspend_state == SUSPEND_DONE); | ||
| 283 | } else { | 342 | } else { |
| 343 | as->suspend_state = SUSPEND_WAIT; | ||
| 344 | mutex_unlock(&state_lock); | ||
| 345 | |||
| 284 | /* | 346 | /* |
| 285 | * Otherwise it is a request to suspend the system. | 347 | * Otherwise it is a request to suspend the system. |
| 286 | * Queue an event for all readers, and expect an | 348 | * Queue an event for all readers, and expect an |
| 287 | * acknowledge from all writers who haven't already | 349 | * acknowledge from all writers who haven't already |
| 288 | * acknowledged. | 350 | * acknowledged. |
| 289 | */ | 351 | */ |
| 290 | queue_event(APM_USER_SUSPEND, as); | 352 | err = queue_suspend_event(APM_USER_SUSPEND, as); |
| 291 | } | 353 | if (err < 0) { |
| 292 | 354 | /* | |
| 293 | /* | 355 | * Avoid taking the lock here - this |
| 294 | * If there are no further acknowledges required, suspend | 356 | * should be fine. |
| 295 | * the system. | 357 | */ |
| 296 | */ | 358 | as->suspend_state = SUSPEND_NONE; |
| 297 | if (suspends_pending == 0) | 359 | break; |
| 298 | apm_suspend(); | 360 | } |
| 361 | |||
| 362 | if (err > 0) | ||
| 363 | apm_suspend(); | ||
| 299 | 364 | ||
| 300 | /* | 365 | /* |
| 301 | * Wait for the suspend/resume to complete. If there are | 366 | * Wait for the suspend/resume to complete. If there |
| 302 | * pending acknowledges, we wait here for them. | 367 | * are pending acknowledges, we wait here for them. |
| 303 | * | 368 | * |
| 304 | * Note that we need to ensure that the PM subsystem does | 369 | * Note: we need to ensure that the PM subsystem does |
| 305 | * not kick us out of the wait when it suspends the threads. | 370 | * not kick us out of the wait when it suspends the |
| 306 | */ | 371 | * threads. |
| 307 | flags = current->flags; | 372 | */ |
| 308 | current->flags |= PF_NOFREEZE; | 373 | flags = current->flags; |
| 374 | current->flags |= PF_NOFREEZE; | ||
| 309 | 375 | ||
| 310 | /* | ||
| 311 | * Note: do not allow a thread which is acking the suspend | ||
| 312 | * to escape until the resume is complete. | ||
| 313 | */ | ||
| 314 | if (as->suspend_state == SUSPEND_ACKED) | ||
| 315 | wait_event(apm_suspend_waitqueue, | ||
| 316 | as->suspend_state == SUSPEND_DONE); | ||
| 317 | else | ||
| 318 | wait_event_interruptible(apm_suspend_waitqueue, | 376 | wait_event_interruptible(apm_suspend_waitqueue, |
| 319 | as->suspend_state == SUSPEND_DONE); | 377 | as->suspend_state == SUSPEND_DONE); |
| 378 | } | ||
| 320 | 379 | ||
| 321 | current->flags = flags; | 380 | current->flags = flags; |
| 381 | |||
| 382 | mutex_lock(&state_lock); | ||
| 322 | err = as->suspend_result; | 383 | err = as->suspend_result; |
| 323 | as->suspend_state = SUSPEND_NONE; | 384 | as->suspend_state = SUSPEND_NONE; |
| 385 | mutex_unlock(&state_lock); | ||
| 324 | break; | 386 | break; |
| 325 | } | 387 | } |
| 326 | 388 | ||
| @@ -330,6 +392,8 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) | |||
| 330 | static int apm_release(struct inode * inode, struct file * filp) | 392 | static int apm_release(struct inode * inode, struct file * filp) |
| 331 | { | 393 | { |
| 332 | struct apm_user *as = filp->private_data; | 394 | struct apm_user *as = filp->private_data; |
| 395 | int pending = 0; | ||
| 396 | |||
| 333 | filp->private_data = NULL; | 397 | filp->private_data = NULL; |
| 334 | 398 | ||
| 335 | down_write(&user_list_lock); | 399 | down_write(&user_list_lock); |
| @@ -342,11 +406,14 @@ static int apm_release(struct inode * inode, struct file * filp) | |||
| 342 | * need to balance suspends_pending, which means the | 406 | * need to balance suspends_pending, which means the |
| 343 | * possibility of sleeping. | 407 | * possibility of sleeping. |
| 344 | */ | 408 | */ |
| 409 | mutex_lock(&state_lock); | ||
| 345 | if (as->suspend_state != SUSPEND_NONE) { | 410 | if (as->suspend_state != SUSPEND_NONE) { |
| 346 | suspends_pending -= 1; | 411 | suspends_pending -= 1; |
| 347 | if (suspends_pending == 0) | 412 | pending = suspends_pending == 0; |
| 348 | apm_suspend(); | ||
| 349 | } | 413 | } |
| 414 | mutex_unlock(&state_lock); | ||
| 415 | if (pending) | ||
| 416 | apm_suspend(); | ||
| 350 | 417 | ||
| 351 | kfree(as); | 418 | kfree(as); |
| 352 | return 0; | 419 | return 0; |
| @@ -470,6 +537,7 @@ static int kapmd(void *arg) | |||
| 470 | { | 537 | { |
| 471 | do { | 538 | do { |
| 472 | apm_event_t event; | 539 | apm_event_t event; |
| 540 | int ret; | ||
| 473 | 541 | ||
| 474 | wait_event_interruptible(kapmd_wait, | 542 | wait_event_interruptible(kapmd_wait, |
| 475 | !queue_empty(&kapmd_queue) || kthread_should_stop()); | 543 | !queue_empty(&kapmd_queue) || kthread_should_stop()); |
| @@ -489,13 +557,20 @@ static int kapmd(void *arg) | |||
| 489 | 557 | ||
| 490 | case APM_LOW_BATTERY: | 558 | case APM_LOW_BATTERY: |
| 491 | case APM_POWER_STATUS_CHANGE: | 559 | case APM_POWER_STATUS_CHANGE: |
| 492 | queue_event(event, NULL); | 560 | queue_event(event); |
| 493 | break; | 561 | break; |
| 494 | 562 | ||
| 495 | case APM_USER_SUSPEND: | 563 | case APM_USER_SUSPEND: |
| 496 | case APM_SYS_SUSPEND: | 564 | case APM_SYS_SUSPEND: |
| 497 | queue_event(event, NULL); | 565 | ret = queue_suspend_event(event, NULL); |
| 498 | if (suspends_pending == 0) | 566 | if (ret < 0) { |
| 567 | /* | ||
| 568 | * We were busy. Try again in 50ms. | ||
| 569 | */ | ||
| 570 | queue_add_event(&kapmd_queue, event); | ||
| 571 | msleep(50); | ||
| 572 | } | ||
| 573 | if (ret > 0) | ||
| 499 | apm_suspend(); | 574 | apm_suspend(); |
| 500 | break; | 575 | break; |
| 501 | 576 | ||
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index cc2d58d028e1..3c078e346753 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <asm/mach/arch.h> | 15 | #include <asm/mach/arch.h> |
| 16 | #include <asm/thread_info.h> | 16 | #include <asm/thread_info.h> |
| 17 | #include <asm/memory.h> | 17 | #include <asm/memory.h> |
| 18 | #include <asm/procinfo.h> | ||
| 18 | 19 | ||
| 19 | /* | 20 | /* |
| 20 | * Make sure that the compiler and target are compatible. | 21 | * Make sure that the compiler and target are compatible. |
diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c index b27513a0f11e..a786f769035d 100644 --- a/arch/arm/kernel/ecard.c +++ b/arch/arm/kernel/ecard.c | |||
| @@ -529,7 +529,7 @@ static void ecard_dump_irq_state(void) | |||
| 529 | } | 529 | } |
| 530 | } | 530 | } |
| 531 | 531 | ||
| 532 | static void ecard_check_lockup(struct irqdesc *desc) | 532 | static void ecard_check_lockup(struct irq_desc *desc) |
| 533 | { | 533 | { |
| 534 | static unsigned long last; | 534 | static unsigned long last; |
| 535 | static int lockup; | 535 | static int lockup; |
| @@ -567,7 +567,7 @@ static void ecard_check_lockup(struct irqdesc *desc) | |||
| 567 | } | 567 | } |
| 568 | 568 | ||
| 569 | static void | 569 | static void |
| 570 | ecard_irq_handler(unsigned int irq, struct irqdesc *desc) | 570 | ecard_irq_handler(unsigned int irq, struct irq_desc *desc) |
| 571 | { | 571 | { |
| 572 | ecard_t *ec; | 572 | ecard_t *ec; |
| 573 | int called = 0; | 573 | int called = 0; |
| @@ -585,7 +585,7 @@ ecard_irq_handler(unsigned int irq, struct irqdesc *desc) | |||
| 585 | pending = ecard_default_ops.irqpending(ec); | 585 | pending = ecard_default_ops.irqpending(ec); |
| 586 | 586 | ||
| 587 | if (pending) { | 587 | if (pending) { |
| 588 | struct irqdesc *d = irq_desc + ec->irq; | 588 | struct irq_desc *d = irq_desc + ec->irq; |
| 589 | desc_handle_irq(ec->irq, d); | 589 | desc_handle_irq(ec->irq, d); |
| 590 | called ++; | 590 | called ++; |
| 591 | } | 591 | } |
| @@ -609,7 +609,7 @@ static unsigned char first_set[] = | |||
| 609 | }; | 609 | }; |
| 610 | 610 | ||
| 611 | static void | 611 | static void |
| 612 | ecard_irqexp_handler(unsigned int irq, struct irqdesc *desc) | 612 | ecard_irqexp_handler(unsigned int irq, struct irq_desc *desc) |
| 613 | { | 613 | { |
| 614 | const unsigned int statusmask = 15; | 614 | const unsigned int statusmask = 15; |
| 615 | unsigned int status; | 615 | unsigned int status; |
| @@ -1022,7 +1022,7 @@ ecard_probe(int slot, card_type_t type) | |||
| 1022 | if (slot < 8) { | 1022 | if (slot < 8) { |
| 1023 | ec->irq = 32 + slot; | 1023 | ec->irq = 32 + slot; |
| 1024 | set_irq_chip(ec->irq, &ecard_chip); | 1024 | set_irq_chip(ec->irq, &ecard_chip); |
| 1025 | set_irq_handler(ec->irq, do_level_IRQ); | 1025 | set_irq_handler(ec->irq, handle_level_irq); |
| 1026 | set_irq_flags(ec->irq, IRQF_VALID); | 1026 | set_irq_flags(ec->irq, IRQF_VALID); |
| 1027 | } | 1027 | } |
| 1028 | 1028 | ||
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S index bd623b73445f..2db42b18f53f 100644 --- a/arch/arm/kernel/entry-armv.S +++ b/arch/arm/kernel/entry-armv.S | |||
| @@ -589,10 +589,6 @@ ENTRY(__switch_to) | |||
| 589 | strex r5, r4, [ip] @ Clear exclusive monitor | 589 | strex r5, r4, [ip] @ Clear exclusive monitor |
| 590 | #endif | 590 | #endif |
| 591 | #endif | 591 | #endif |
| 592 | #if defined(CONFIG_CPU_XSCALE) && !defined(CONFIG_IWMMXT) | ||
| 593 | mra r4, r5, acc0 | ||
| 594 | stmia ip, {r4, r5} | ||
| 595 | #endif | ||
| 596 | #if defined(CONFIG_HAS_TLS_REG) | 592 | #if defined(CONFIG_HAS_TLS_REG) |
| 597 | mcr p15, 0, r3, c13, c0, 3 @ set TLS register | 593 | mcr p15, 0, r3, c13, c0, 3 @ set TLS register |
| 598 | #elif !defined(CONFIG_TLS_REG_EMUL) | 594 | #elif !defined(CONFIG_TLS_REG_EMUL) |
| @@ -602,11 +598,6 @@ ENTRY(__switch_to) | |||
| 602 | #ifdef CONFIG_MMU | 598 | #ifdef CONFIG_MMU |
| 603 | mcr p15, 0, r6, c3, c0, 0 @ Set domain register | 599 | mcr p15, 0, r6, c3, c0, 0 @ Set domain register |
| 604 | #endif | 600 | #endif |
| 605 | #if defined(CONFIG_CPU_XSCALE) && !defined(CONFIG_IWMMXT) | ||
| 606 | add r4, r2, #TI_CPU_DOMAIN + 40 @ cpu_context_save->extra | ||
| 607 | ldmib r4, {r4, r5} | ||
| 608 | mar acc0, r4, r5 | ||
| 609 | #endif | ||
| 610 | mov r5, r0 | 601 | mov r5, r0 |
| 611 | add r4, r2, #TI_CPU_SAVE | 602 | add r4, r2, #TI_CPU_SAVE |
| 612 | ldr r0, =thread_notify_head | 603 | ldr r0, =thread_notify_head |
diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S index f359a189dcf2..0119c0d5f978 100644 --- a/arch/arm/kernel/head-nommu.S +++ b/arch/arm/kernel/head-nommu.S | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | 16 | ||
| 17 | #include <asm/assembler.h> | 17 | #include <asm/assembler.h> |
| 18 | #include <asm/mach-types.h> | 18 | #include <asm/mach-types.h> |
| 19 | #include <asm/procinfo.h> | ||
| 20 | #include <asm/ptrace.h> | 19 | #include <asm/ptrace.h> |
| 21 | #include <asm/asm-offsets.h> | 20 | #include <asm/asm-offsets.h> |
| 22 | #include <asm/thread_info.h> | 21 | #include <asm/thread_info.h> |
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index ebc3e74a7947..bda0748ffb00 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | 16 | ||
| 17 | #include <asm/assembler.h> | 17 | #include <asm/assembler.h> |
| 18 | #include <asm/domain.h> | 18 | #include <asm/domain.h> |
| 19 | #include <asm/procinfo.h> | ||
| 20 | #include <asm/ptrace.h> | 19 | #include <asm/ptrace.h> |
| 21 | #include <asm/asm-offsets.h> | 20 | #include <asm/asm-offsets.h> |
| 22 | #include <asm/memory.h> | 21 | #include <asm/memory.h> |
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 2c4ff1cbe334..ec01f08f5642 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c | |||
| @@ -112,7 +112,7 @@ static struct irq_desc bad_irq_desc = { | |||
| 112 | asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) | 112 | asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) |
| 113 | { | 113 | { |
| 114 | struct pt_regs *old_regs = set_irq_regs(regs); | 114 | struct pt_regs *old_regs = set_irq_regs(regs); |
| 115 | struct irqdesc *desc = irq_desc + irq; | 115 | struct irq_desc *desc = irq_desc + irq; |
| 116 | 116 | ||
| 117 | /* | 117 | /* |
| 118 | * Some hardware gives randomly wrong interrupts. Rather | 118 | * Some hardware gives randomly wrong interrupts. Rather |
| @@ -134,7 +134,7 @@ asmlinkage void asm_do_IRQ(unsigned int irq, struct pt_regs *regs) | |||
| 134 | 134 | ||
| 135 | void set_irq_flags(unsigned int irq, unsigned int iflags) | 135 | void set_irq_flags(unsigned int irq, unsigned int iflags) |
| 136 | { | 136 | { |
| 137 | struct irqdesc *desc; | 137 | struct irq_desc *desc; |
| 138 | unsigned long flags; | 138 | unsigned long flags; |
| 139 | 139 | ||
| 140 | if (irq >= NR_IRQS) { | 140 | if (irq >= NR_IRQS) { |
| @@ -171,7 +171,7 @@ void __init init_IRQ(void) | |||
| 171 | 171 | ||
| 172 | #ifdef CONFIG_HOTPLUG_CPU | 172 | #ifdef CONFIG_HOTPLUG_CPU |
| 173 | 173 | ||
| 174 | static void route_irq(struct irqdesc *desc, unsigned int irq, unsigned int cpu) | 174 | static void route_irq(struct irq_desc *desc, unsigned int irq, unsigned int cpu) |
| 175 | { | 175 | { |
| 176 | pr_debug("IRQ%u: moving from cpu%u to cpu%u\n", irq, desc->cpu, cpu); | 176 | pr_debug("IRQ%u: moving from cpu%u to cpu%u\n", irq, desc->cpu, cpu); |
| 177 | 177 | ||
| @@ -190,7 +190,7 @@ void migrate_irqs(void) | |||
| 190 | unsigned int i, cpu = smp_processor_id(); | 190 | unsigned int i, cpu = smp_processor_id(); |
| 191 | 191 | ||
| 192 | for (i = 0; i < NR_IRQS; i++) { | 192 | for (i = 0; i < NR_IRQS; i++) { |
| 193 | struct irqdesc *desc = irq_desc + i; | 193 | struct irq_desc *desc = irq_desc + i; |
| 194 | 194 | ||
| 195 | if (desc->cpu == cpu) { | 195 | if (desc->cpu == cpu) { |
| 196 | unsigned int newcpu = any_online_cpu(desc->affinity); | 196 | unsigned int newcpu = any_online_cpu(desc->affinity); |
diff --git a/arch/arm/kernel/iwmmxt-notifier.c b/arch/arm/kernel/iwmmxt-notifier.c deleted file mode 100644 index 0d1a1db40062..000000000000 --- a/arch/arm/kernel/iwmmxt-notifier.c +++ /dev/null | |||
| @@ -1,63 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/kernel/iwmmxt-notifier.c | ||
| 3 | * | ||
| 4 | * XScale iWMMXt (Concan) context switching and handling | ||
| 5 | * | ||
| 6 | * Initial code: | ||
| 7 | * Copyright (c) 2003, Intel Corporation | ||
| 8 | * | ||
| 9 | * Full lazy switching support, optimizations and more, by Nicolas Pitre | ||
| 10 | * Copyright (c) 2003-2004, MontaVista Software, Inc. | ||
| 11 | * | ||
| 12 | * This program is free software; you can redistribute it and/or modify | ||
| 13 | * it under the terms of the GNU General Public License version 2 as | ||
| 14 | * published by the Free Software Foundation. | ||
| 15 | */ | ||
| 16 | |||
| 17 | #include <linux/module.h> | ||
| 18 | #include <linux/types.h> | ||
| 19 | #include <linux/kernel.h> | ||
| 20 | #include <linux/signal.h> | ||
| 21 | #include <linux/sched.h> | ||
| 22 | #include <linux/init.h> | ||
| 23 | #include <asm/thread_notify.h> | ||
| 24 | #include <asm/io.h> | ||
| 25 | |||
| 26 | static int iwmmxt_do(struct notifier_block *self, unsigned long cmd, void *t) | ||
| 27 | { | ||
| 28 | struct thread_info *thread = t; | ||
| 29 | |||
| 30 | switch (cmd) { | ||
| 31 | case THREAD_NOTIFY_FLUSH: | ||
| 32 | /* | ||
| 33 | * flush_thread() zeroes thread->fpstate, so no need | ||
| 34 | * to do anything here. | ||
| 35 | * | ||
| 36 | * FALLTHROUGH: Ensure we don't try to overwrite our newly | ||
| 37 | * initialised state information on the first fault. | ||
| 38 | */ | ||
| 39 | |||
| 40 | case THREAD_NOTIFY_RELEASE: | ||
| 41 | iwmmxt_task_release(thread); | ||
| 42 | break; | ||
| 43 | |||
| 44 | case THREAD_NOTIFY_SWITCH: | ||
| 45 | iwmmxt_task_switch(thread); | ||
| 46 | break; | ||
| 47 | } | ||
| 48 | |||
| 49 | return NOTIFY_DONE; | ||
| 50 | } | ||
| 51 | |||
| 52 | static struct notifier_block iwmmxt_notifier_block = { | ||
| 53 | .notifier_call = iwmmxt_do, | ||
| 54 | }; | ||
| 55 | |||
| 56 | static int __init iwmmxt_init(void) | ||
| 57 | { | ||
| 58 | thread_register_notifier(&iwmmxt_notifier_block); | ||
| 59 | |||
| 60 | return 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | late_initcall(iwmmxt_init); | ||
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index bf35c178a877..a9e8f7e55fd6 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c | |||
| @@ -281,67 +281,6 @@ void show_fpregs(struct user_fp *regs) | |||
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | /* | 283 | /* |
| 284 | * Task structure and kernel stack allocation. | ||
| 285 | */ | ||
| 286 | struct thread_info_list { | ||
| 287 | unsigned long *head; | ||
| 288 | unsigned int nr; | ||
| 289 | }; | ||
| 290 | |||
| 291 | static DEFINE_PER_CPU(struct thread_info_list, thread_info_list) = { NULL, 0 }; | ||
| 292 | |||
| 293 | #define EXTRA_TASK_STRUCT 4 | ||
| 294 | |||
| 295 | struct thread_info *alloc_thread_info(struct task_struct *task) | ||
| 296 | { | ||
| 297 | struct thread_info *thread = NULL; | ||
| 298 | |||
| 299 | if (EXTRA_TASK_STRUCT) { | ||
| 300 | struct thread_info_list *th = &get_cpu_var(thread_info_list); | ||
| 301 | unsigned long *p = th->head; | ||
| 302 | |||
| 303 | if (p) { | ||
| 304 | th->head = (unsigned long *)p[0]; | ||
| 305 | th->nr -= 1; | ||
| 306 | } | ||
| 307 | put_cpu_var(thread_info_list); | ||
| 308 | |||
| 309 | thread = (struct thread_info *)p; | ||
| 310 | } | ||
| 311 | |||
| 312 | if (!thread) | ||
| 313 | thread = (struct thread_info *) | ||
| 314 | __get_free_pages(GFP_KERNEL, THREAD_SIZE_ORDER); | ||
| 315 | |||
| 316 | #ifdef CONFIG_DEBUG_STACK_USAGE | ||
| 317 | /* | ||
| 318 | * The stack must be cleared if you want SYSRQ-T to | ||
| 319 | * give sensible stack usage information | ||
| 320 | */ | ||
| 321 | if (thread) | ||
| 322 | memzero(thread, THREAD_SIZE); | ||
| 323 | #endif | ||
| 324 | return thread; | ||
| 325 | } | ||
| 326 | |||
| 327 | void free_thread_info(struct thread_info *thread) | ||
| 328 | { | ||
| 329 | if (EXTRA_TASK_STRUCT) { | ||
| 330 | struct thread_info_list *th = &get_cpu_var(thread_info_list); | ||
| 331 | if (th->nr < EXTRA_TASK_STRUCT) { | ||
| 332 | unsigned long *p = (unsigned long *)thread; | ||
| 333 | p[0] = (unsigned long)th->head; | ||
| 334 | th->head = p; | ||
| 335 | th->nr += 1; | ||
| 336 | put_cpu_var(thread_info_list); | ||
| 337 | return; | ||
| 338 | } | ||
| 339 | put_cpu_var(thread_info_list); | ||
| 340 | } | ||
| 341 | free_pages((unsigned long)thread, THREAD_SIZE_ORDER); | ||
| 342 | } | ||
| 343 | |||
| 344 | /* | ||
| 345 | * Free current thread data structures etc.. | 284 | * Free current thread data structures etc.. |
| 346 | */ | 285 | */ |
| 347 | void exit_thread(void) | 286 | void exit_thread(void) |
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 29efc9f82057..238dd9b6db84 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c | |||
| @@ -357,9 +357,6 @@ static void __init setup_processor(void) | |||
| 357 | #ifndef CONFIG_VFP | 357 | #ifndef CONFIG_VFP |
| 358 | elf_hwcap &= ~HWCAP_VFP; | 358 | elf_hwcap &= ~HWCAP_VFP; |
| 359 | #endif | 359 | #endif |
| 360 | #ifndef CONFIG_IWMMXT | ||
| 361 | elf_hwcap &= ~HWCAP_IWMMXT; | ||
| 362 | #endif | ||
| 363 | 360 | ||
| 364 | cpu_proc_init(); | 361 | cpu_proc_init(); |
| 365 | } | 362 | } |
| @@ -441,16 +438,19 @@ __early_param("initrd=", early_initrd); | |||
| 441 | 438 | ||
| 442 | static void __init arm_add_memory(unsigned long start, unsigned long size) | 439 | static void __init arm_add_memory(unsigned long start, unsigned long size) |
| 443 | { | 440 | { |
| 441 | struct membank *bank; | ||
| 442 | |||
| 444 | /* | 443 | /* |
| 445 | * Ensure that start/size are aligned to a page boundary. | 444 | * Ensure that start/size are aligned to a page boundary. |
| 446 | * Size is appropriately rounded down, start is rounded up. | 445 | * Size is appropriately rounded down, start is rounded up. |
| 447 | */ | 446 | */ |
| 448 | size -= start & ~PAGE_MASK; | 447 | size -= start & ~PAGE_MASK; |
| 449 | 448 | ||
| 450 | meminfo.bank[meminfo.nr_banks].start = PAGE_ALIGN(start); | 449 | bank = &meminfo.bank[meminfo.nr_banks++]; |
| 451 | meminfo.bank[meminfo.nr_banks].size = size & PAGE_MASK; | 450 | |
| 452 | meminfo.bank[meminfo.nr_banks].node = PHYS_TO_NID(start); | 451 | bank->start = PAGE_ALIGN(start); |
| 453 | meminfo.nr_banks += 1; | 452 | bank->size = size & PAGE_MASK; |
| 453 | bank->node = PHYS_TO_NID(start); | ||
| 454 | } | 454 | } |
| 455 | 455 | ||
| 456 | /* | 456 | /* |
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 48cf7fffddf2..f2b1d61fbc0e 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <linux/ptrace.h> | 12 | #include <linux/ptrace.h> |
| 13 | #include <linux/personality.h> | 13 | #include <linux/personality.h> |
| 14 | 14 | ||
| 15 | #include <asm/elf.h> | ||
| 15 | #include <asm/cacheflush.h> | 16 | #include <asm/cacheflush.h> |
| 16 | #include <asm/ucontext.h> | 17 | #include <asm/ucontext.h> |
| 17 | #include <asm/uaccess.h> | 18 | #include <asm/uaccess.h> |
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index bede380c07a9..042a12982e98 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
| @@ -631,12 +631,9 @@ baddataabort(int code, unsigned long instr, struct pt_regs *regs) | |||
| 631 | notify_die("unknown data abort code", regs, &info, instr, 0); | 631 | notify_die("unknown data abort code", regs, &info, instr, 0); |
| 632 | } | 632 | } |
| 633 | 633 | ||
| 634 | void __attribute__((noreturn)) __bug(const char *file, int line, void *data) | 634 | void __attribute__((noreturn)) __bug(const char *file, int line) |
| 635 | { | 635 | { |
| 636 | printk(KERN_CRIT"kernel BUG at %s:%d!", file, line); | 636 | printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line); |
| 637 | if (data) | ||
| 638 | printk(" - extra data = %p", data); | ||
| 639 | printk("\n"); | ||
| 640 | *(int *)0 = 0; | 637 | *(int *)0 = 0; |
| 641 | 638 | ||
| 642 | /* Avoid "noreturn function does return" */ | 639 | /* Avoid "noreturn function does return" */ |
diff --git a/arch/arm/kernel/xscale-cp0.c b/arch/arm/kernel/xscale-cp0.c new file mode 100644 index 000000000000..180000bfdc8f --- /dev/null +++ b/arch/arm/kernel/xscale-cp0.c | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | /* | ||
| 2 | * linux/arch/arm/kernel/xscale-cp0.c | ||
| 3 | * | ||
| 4 | * XScale DSP and iWMMXt coprocessor context switching and handling | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/module.h> | ||
| 12 | #include <linux/types.h> | ||
| 13 | #include <linux/kernel.h> | ||
| 14 | #include <linux/signal.h> | ||
| 15 | #include <linux/sched.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <asm/thread_notify.h> | ||
| 18 | #include <asm/io.h> | ||
| 19 | |||
| 20 | static inline void dsp_save_state(u32 *state) | ||
| 21 | { | ||
| 22 | __asm__ __volatile__ ( | ||
| 23 | "mrrc p0, 0, %0, %1, c0\n" | ||
| 24 | : "=r" (state[0]), "=r" (state[1])); | ||
| 25 | } | ||
| 26 | |||
| 27 | static inline void dsp_load_state(u32 *state) | ||
| 28 | { | ||
| 29 | __asm__ __volatile__ ( | ||
| 30 | "mcrr p0, 0, %0, %1, c0\n" | ||
| 31 | : : "r" (state[0]), "r" (state[1])); | ||
| 32 | } | ||
| 33 | |||
| 34 | static int dsp_do(struct notifier_block *self, unsigned long cmd, void *t) | ||
| 35 | { | ||
| 36 | struct thread_info *thread = t; | ||
| 37 | |||
| 38 | switch (cmd) { | ||
| 39 | case THREAD_NOTIFY_FLUSH: | ||
| 40 | thread->cpu_context.extra[0] = 0; | ||
| 41 | thread->cpu_context.extra[1] = 0; | ||
| 42 | break; | ||
| 43 | |||
| 44 | case THREAD_NOTIFY_SWITCH: | ||
| 45 | dsp_save_state(current_thread_info()->cpu_context.extra); | ||
| 46 | dsp_load_state(thread->cpu_context.extra); | ||
| 47 | break; | ||
| 48 | } | ||
| 49 | |||
| 50 | return NOTIFY_DONE; | ||
| 51 | } | ||
| 52 | |||
| 53 | static struct notifier_block dsp_notifier_block = { | ||
| 54 | .notifier_call = dsp_do, | ||
| 55 | }; | ||
| 56 | |||
| 57 | |||
| 58 | #ifdef CONFIG_IWMMXT | ||
| 59 | static int iwmmxt_do(struct notifier_block *self, unsigned long cmd, void *t) | ||
| 60 | { | ||
| 61 | struct thread_info *thread = t; | ||
| 62 | |||
| 63 | switch (cmd) { | ||
| 64 | case THREAD_NOTIFY_FLUSH: | ||
| 65 | /* | ||
| 66 | * flush_thread() zeroes thread->fpstate, so no need | ||
| 67 | * to do anything here. | ||
| 68 | * | ||
| 69 | * FALLTHROUGH: Ensure we don't try to overwrite our newly | ||
| 70 | * initialised state information on the first fault. | ||
| 71 | */ | ||
| 72 | |||
| 73 | case THREAD_NOTIFY_RELEASE: | ||
| 74 | iwmmxt_task_release(thread); | ||
| 75 | break; | ||
| 76 | |||
| 77 | case THREAD_NOTIFY_SWITCH: | ||
| 78 | iwmmxt_task_switch(thread); | ||
| 79 | break; | ||
| 80 | } | ||
| 81 | |||
| 82 | return NOTIFY_DONE; | ||
| 83 | } | ||
| 84 | |||
| 85 | static struct notifier_block iwmmxt_notifier_block = { | ||
| 86 | .notifier_call = iwmmxt_do, | ||
| 87 | }; | ||
| 88 | #endif | ||
| 89 | |||
| 90 | |||
| 91 | static u32 __init xscale_cp_access_read(void) | ||
| 92 | { | ||
| 93 | u32 value; | ||
| 94 | |||
| 95 | __asm__ __volatile__ ( | ||
| 96 | "mrc p15, 0, %0, c15, c1, 0\n\t" | ||
| 97 | : "=r" (value)); | ||
| 98 | |||
| 99 | return value; | ||
| 100 | } | ||
| 101 | |||
| 102 | static void __init xscale_cp_access_write(u32 value) | ||
| 103 | { | ||
| 104 | u32 temp; | ||
| 105 | |||
| 106 | __asm__ __volatile__ ( | ||
| 107 | "mcr p15, 0, %1, c15, c1, 0\n\t" | ||
| 108 | "mrc p15, 0, %0, c15, c1, 0\n\t" | ||
| 109 | "mov %0, %0\n\t" | ||
| 110 | "sub pc, pc, #4\n\t" | ||
| 111 | : "=r" (temp) : "r" (value)); | ||
| 112 | } | ||
| 113 | |||
| 114 | /* | ||
| 115 | * Detect whether we have a MAC coprocessor (40 bit register) or an | ||
| 116 | * iWMMXt coprocessor (64 bit registers) by loading 00000100:00000000 | ||
| 117 | * into a coprocessor register and reading it back, and checking | ||
| 118 | * whether the upper word survived intact. | ||
| 119 | */ | ||
| 120 | static int __init cpu_has_iwmmxt(void) | ||
| 121 | { | ||
| 122 | u32 lo; | ||
| 123 | u32 hi; | ||
| 124 | |||
| 125 | /* | ||
| 126 | * This sequence is interpreted by the DSP coprocessor as: | ||
| 127 | * mar acc0, %2, %3 | ||
| 128 | * mra %0, %1, acc0 | ||
| 129 | * | ||
| 130 | * And by the iWMMXt coprocessor as: | ||
| 131 | * tmcrr wR0, %2, %3 | ||
| 132 | * tmrrc %0, %1, wR0 | ||
| 133 | */ | ||
| 134 | __asm__ __volatile__ ( | ||
| 135 | "mcrr p0, 0, %2, %3, c0\n" | ||
| 136 | "mrrc p0, 0, %0, %1, c0\n" | ||
| 137 | : "=r" (lo), "=r" (hi) | ||
| 138 | : "r" (0), "r" (0x100)); | ||
| 139 | |||
| 140 | return !!hi; | ||
| 141 | } | ||
| 142 | |||
| 143 | |||
| 144 | /* | ||
| 145 | * If we detect that the CPU has iWMMXt (and CONFIG_IWMMXT=y), we | ||
| 146 | * disable CP0/CP1 on boot, and let call_fpe() and the iWMMXt lazy | ||
| 147 | * switch code handle iWMMXt context switching. If on the other | ||
| 148 | * hand the CPU has a DSP coprocessor, we keep access to CP0 enabled | ||
| 149 | * all the time, and save/restore acc0 on context switch in non-lazy | ||
| 150 | * fashion. | ||
| 151 | */ | ||
| 152 | static int __init xscale_cp0_init(void) | ||
| 153 | { | ||
| 154 | u32 cp_access; | ||
| 155 | |||
| 156 | cp_access = xscale_cp_access_read() & ~3; | ||
| 157 | xscale_cp_access_write(cp_access | 1); | ||
| 158 | |||
| 159 | if (cpu_has_iwmmxt()) { | ||
| 160 | #ifndef CONFIG_IWMMXT | ||
| 161 | printk(KERN_WARNING "CAUTION: XScale iWMMXt coprocessor " | ||
| 162 | "detected, but kernel support is missing.\n"); | ||
| 163 | #else | ||
| 164 | printk(KERN_INFO "XScale iWMMXt coprocessor detected.\n"); | ||
| 165 | elf_hwcap |= HWCAP_IWMMXT; | ||
| 166 | thread_register_notifier(&iwmmxt_notifier_block); | ||
| 167 | #endif | ||
| 168 | } else { | ||
| 169 | printk(KERN_INFO "XScale DSP coprocessor detected.\n"); | ||
| 170 | thread_register_notifier(&dsp_notifier_block); | ||
| 171 | cp_access |= 1; | ||
| 172 | } | ||
| 173 | |||
| 174 | xscale_cp_access_write(cp_access); | ||
| 175 | |||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | |||
| 179 | late_initcall(xscale_cp0_init); | ||
