pm.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. * Lemote loongson2f family machines' specific suspend support
  3. *
  4. * Copyright (C) 2009 Lemote Inc.
  5. * Author: Wu Zhangjin <wuzj@lemote.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. */
  12. #include <linux/suspend.h>
  13. #include <linux/interrupt.h>
  14. #include <linux/pm.h>
  15. #include <linux/i8042.h>
  16. #include <linux/module.h>
  17. #include <asm/i8259.h>
  18. #include <asm/mipsregs.h>
  19. #include <asm/bootinfo.h>
  20. #include <loongson.h>
  21. #include "ec_kb3310b.h"
  22. #define I8042_KBD_IRQ 1
  23. #define I8042_CTR_KBDINT 0x01
  24. #define I8042_CTR_KBDDIS 0x10
  25. static unsigned char i8042_ctr;
  26. static int i8042_enable_kbd_port(void)
  27. {
  28. if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
  29. pr_err("i8042.c: Can't read CTR while enabling i8042 kbd port."
  30. "\n");
  31. return -EIO;
  32. }
  33. i8042_ctr &= ~I8042_CTR_KBDDIS;
  34. i8042_ctr |= I8042_CTR_KBDINT;
  35. if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
  36. i8042_ctr &= ~I8042_CTR_KBDINT;
  37. i8042_ctr |= I8042_CTR_KBDDIS;
  38. pr_err("i8042.c: Failed to enable KBD port.\n");
  39. return -EIO;
  40. }
  41. return 0;
  42. }
  43. void setup_wakeup_events(void)
  44. {
  45. int irq_mask;
  46. switch (mips_machtype) {
  47. case MACH_LEMOTE_ML2F7:
  48. case MACH_LEMOTE_YL2F89:
  49. /* open the keyboard irq in i8259A */
  50. outb((0xff & ~(1 << I8042_KBD_IRQ)), PIC_MASTER_IMR);
  51. irq_mask = inb(PIC_MASTER_IMR);
  52. /* enable keyboard port */
  53. i8042_enable_kbd_port();
  54. /* Wakeup CPU via SCI lid open event */
  55. outb(irq_mask & ~(1 << PIC_CASCADE_IR), PIC_MASTER_IMR);
  56. inb(PIC_MASTER_IMR);
  57. outb(0xff & ~(1 << (SCI_IRQ_NUM - 8)), PIC_SLAVE_IMR);
  58. inb(PIC_SLAVE_IMR);
  59. break;
  60. default:
  61. break;
  62. }
  63. }
  64. static struct delayed_work lid_task;
  65. static int initialized;
  66. /* yeeloong_report_lid_status will be implemented in yeeloong_laptop.c */
  67. sci_handler yeeloong_report_lid_status;
  68. EXPORT_SYMBOL(yeeloong_report_lid_status);
  69. static void yeeloong_lid_update_task(struct work_struct *work)
  70. {
  71. if (yeeloong_report_lid_status)
  72. yeeloong_report_lid_status(BIT_LID_DETECT_ON);
  73. }
  74. int wakeup_loongson(void)
  75. {
  76. int irq;
  77. /* query the interrupt number */
  78. irq = mach_i8259_irq();
  79. if (irq < 0)
  80. return 0;
  81. printk(KERN_INFO "%s: irq = %d\n", __func__, irq);
  82. if (irq == I8042_KBD_IRQ)
  83. return 1;
  84. else if (irq == SCI_IRQ_NUM) {
  85. int ret, sci_event;
  86. /* query the event number */
  87. ret = ec_query_seq(CMD_GET_EVENT_NUM);
  88. if (ret < 0)
  89. return 0;
  90. sci_event = ec_get_event_num();
  91. if (sci_event < 0)
  92. return 0;
  93. if (sci_event == EVENT_LID) {
  94. int lid_status;
  95. /* check the LID status */
  96. lid_status = ec_read(REG_LID_DETECT);
  97. /* wakeup cpu when people open the LID */
  98. if (lid_status == BIT_LID_DETECT_ON) {
  99. /* If we call it directly here, the WARNING
  100. * will be sent out by getnstimeofday
  101. * via "WARN_ON(timekeeping_suspended);"
  102. * because we can not schedule in suspend mode.
  103. */
  104. if (initialized == 0) {
  105. INIT_DELAYED_WORK(&lid_task,
  106. yeeloong_lid_update_task);
  107. initialized = 1;
  108. }
  109. schedule_delayed_work(&lid_task, 1);
  110. return 1;
  111. }
  112. }
  113. }
  114. return 0;
  115. }