diff options
| -rw-r--r-- | arch/x86/Kconfig | 2 | ||||
| -rw-r--r-- | arch/x86/include/asm/olpc.h | 5 | ||||
| -rw-r--r-- | arch/x86/platform/olpc/olpc-xo1-sci.c | 183 | ||||
| -rw-r--r-- | include/linux/cs5535.h | 22 |
4 files changed, 209 insertions, 3 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 66b7b9d519d5..88889106ac9b 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
| @@ -2087,7 +2087,9 @@ config OLPC_XO1_SCI | |||
| 2087 | select MFD_CORE | 2087 | select MFD_CORE |
| 2088 | ---help--- | 2088 | ---help--- |
| 2089 | Add support for SCI-based features of the OLPC XO-1 laptop: | 2089 | Add support for SCI-based features of the OLPC XO-1 laptop: |
| 2090 | - EC-driven system wakeups | ||
| 2090 | - Power button | 2091 | - Power button |
| 2092 | - Ebook switch | ||
| 2091 | 2093 | ||
| 2092 | endif # X86_32 | 2094 | endif # X86_32 |
| 2093 | 2095 | ||
diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h index 0e56d01907fe..87bdbca72f94 100644 --- a/arch/x86/include/asm/olpc.h +++ b/arch/x86/include/asm/olpc.h | |||
| @@ -111,6 +111,7 @@ extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, | |||
| 111 | #define EC_WRITE_SCI_MASK 0x1b | 111 | #define EC_WRITE_SCI_MASK 0x1b |
| 112 | #define EC_WAKE_UP_WLAN 0x24 | 112 | #define EC_WAKE_UP_WLAN 0x24 |
| 113 | #define EC_WLAN_LEAVE_RESET 0x25 | 113 | #define EC_WLAN_LEAVE_RESET 0x25 |
| 114 | #define EC_READ_EB_MODE 0x2a | ||
| 114 | #define EC_SET_SCI_INHIBIT 0x32 | 115 | #define EC_SET_SCI_INHIBIT 0x32 |
| 115 | #define EC_SET_SCI_INHIBIT_RELEASE 0x34 | 116 | #define EC_SET_SCI_INHIBIT_RELEASE 0x34 |
| 116 | #define EC_WLAN_ENTER_RESET 0x35 | 117 | #define EC_WLAN_ENTER_RESET 0x35 |
| @@ -144,7 +145,7 @@ extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, | |||
| 144 | #define OLPC_GPIO_SMB_CLK 14 | 145 | #define OLPC_GPIO_SMB_CLK 14 |
| 145 | #define OLPC_GPIO_SMB_DATA 15 | 146 | #define OLPC_GPIO_SMB_DATA 15 |
| 146 | #define OLPC_GPIO_WORKAUX geode_gpio(24) | 147 | #define OLPC_GPIO_WORKAUX geode_gpio(24) |
| 147 | #define OLPC_GPIO_LID geode_gpio(26) | 148 | #define OLPC_GPIO_LID 26 |
| 148 | #define OLPC_GPIO_ECSCI geode_gpio(27) | 149 | #define OLPC_GPIO_ECSCI 27 |
| 149 | 150 | ||
| 150 | #endif /* _ASM_X86_OLPC_H */ | 151 | #endif /* _ASM_X86_OLPC_H */ |
diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c index 8fbf961dae89..63f50506078b 100644 --- a/arch/x86/platform/olpc/olpc-xo1-sci.c +++ b/arch/x86/platform/olpc/olpc-xo1-sci.c | |||
| @@ -12,12 +12,15 @@ | |||
| 12 | */ | 12 | */ |
| 13 | 13 | ||
| 14 | #include <linux/cs5535.h> | 14 | #include <linux/cs5535.h> |
| 15 | #include <linux/device.h> | ||
| 16 | #include <linux/gpio.h> | ||
| 15 | #include <linux/input.h> | 17 | #include <linux/input.h> |
| 16 | #include <linux/interrupt.h> | 18 | #include <linux/interrupt.h> |
| 17 | #include <linux/platform_device.h> | 19 | #include <linux/platform_device.h> |
| 18 | #include <linux/pm.h> | 20 | #include <linux/pm.h> |
| 19 | #include <linux/mfd/core.h> | 21 | #include <linux/mfd/core.h> |
| 20 | #include <linux/suspend.h> | 22 | #include <linux/suspend.h> |
| 23 | #include <linux/workqueue.h> | ||
| 21 | 24 | ||
| 22 | #include <asm/io.h> | 25 | #include <asm/io.h> |
| 23 | #include <asm/msr.h> | 26 | #include <asm/msr.h> |
| @@ -28,8 +31,60 @@ | |||
| 28 | 31 | ||
| 29 | static unsigned long acpi_base; | 32 | static unsigned long acpi_base; |
| 30 | static struct input_dev *power_button_idev; | 33 | static struct input_dev *power_button_idev; |
| 34 | static struct input_dev *ebook_switch_idev; | ||
| 35 | |||
| 31 | static int sci_irq; | 36 | static int sci_irq; |
| 32 | 37 | ||
| 38 | /* Report current ebook switch state through input layer */ | ||
| 39 | static void send_ebook_state(void) | ||
| 40 | { | ||
| 41 | unsigned char state; | ||
| 42 | |||
| 43 | if (olpc_ec_cmd(EC_READ_EB_MODE, NULL, 0, &state, 1)) { | ||
| 44 | pr_err(PFX "failed to get ebook state\n"); | ||
| 45 | return; | ||
| 46 | } | ||
| 47 | |||
| 48 | input_report_switch(ebook_switch_idev, SW_TABLET_MODE, state); | ||
| 49 | input_sync(ebook_switch_idev); | ||
| 50 | } | ||
| 51 | |||
| 52 | /* | ||
| 53 | * Process all items in the EC's SCI queue. | ||
| 54 | * | ||
| 55 | * This is handled in a workqueue because olpc_ec_cmd can be slow (and | ||
| 56 | * can even timeout). | ||
| 57 | * | ||
| 58 | * If propagate_events is false, the queue is drained without events being | ||
| 59 | * generated for the interrupts. | ||
| 60 | */ | ||
| 61 | static void process_sci_queue(bool propagate_events) | ||
| 62 | { | ||
| 63 | int r; | ||
| 64 | u16 data; | ||
| 65 | |||
| 66 | do { | ||
| 67 | r = olpc_ec_sci_query(&data); | ||
| 68 | if (r || !data) | ||
| 69 | break; | ||
| 70 | |||
| 71 | pr_debug(PFX "SCI 0x%x received\n", data); | ||
| 72 | |||
| 73 | if (data == EC_SCI_SRC_EBOOK && propagate_events) | ||
| 74 | send_ebook_state(); | ||
| 75 | } while (data); | ||
| 76 | |||
| 77 | if (r) | ||
| 78 | pr_err(PFX "Failed to clear SCI queue"); | ||
| 79 | } | ||
| 80 | |||
| 81 | static void process_sci_queue_work(struct work_struct *work) | ||
| 82 | { | ||
| 83 | process_sci_queue(true); | ||
| 84 | } | ||
| 85 | |||
| 86 | static DECLARE_WORK(sci_work, process_sci_queue_work); | ||
| 87 | |||
| 33 | static irqreturn_t xo1_sci_intr(int irq, void *dev_id) | 88 | static irqreturn_t xo1_sci_intr(int irq, void *dev_id) |
| 34 | { | 89 | { |
| 35 | struct platform_device *pdev = dev_id; | 90 | struct platform_device *pdev = dev_id; |
| @@ -51,6 +106,11 @@ static irqreturn_t xo1_sci_intr(int irq, void *dev_id) | |||
| 51 | input_sync(power_button_idev); | 106 | input_sync(power_button_idev); |
| 52 | } | 107 | } |
| 53 | 108 | ||
| 109 | if (gpe & CS5536_GPIOM7_PME_FLAG) { /* EC GPIO */ | ||
| 110 | cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS); | ||
| 111 | schedule_work(&sci_work); | ||
| 112 | } | ||
| 113 | |||
| 54 | return IRQ_HANDLED; | 114 | return IRQ_HANDLED; |
| 55 | } | 115 | } |
| 56 | 116 | ||
| @@ -60,6 +120,19 @@ static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state) | |||
| 60 | olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN); | 120 | olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN); |
| 61 | else | 121 | else |
| 62 | olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN); | 122 | olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN); |
| 123 | |||
| 124 | if (device_may_wakeup(&ebook_switch_idev->dev)) | ||
| 125 | olpc_ec_wakeup_set(EC_SCI_SRC_EBOOK); | ||
| 126 | else | ||
| 127 | olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK); | ||
| 128 | |||
| 129 | return 0; | ||
| 130 | } | ||
| 131 | |||
| 132 | static int xo1_sci_resume(struct platform_device *pdev) | ||
| 133 | { | ||
| 134 | /* Enable all EC events */ | ||
| 135 | olpc_ec_mask_write(EC_SCI_SRC_ALL); | ||
| 63 | return 0; | 136 | return 0; |
| 64 | } | 137 | } |
| 65 | 138 | ||
| @@ -104,6 +177,50 @@ static int __devinit setup_sci_interrupt(struct platform_device *pdev) | |||
| 104 | return r; | 177 | return r; |
| 105 | } | 178 | } |
| 106 | 179 | ||
| 180 | static int __devinit setup_ec_sci(void) | ||
| 181 | { | ||
| 182 | int r; | ||
| 183 | |||
| 184 | r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI"); | ||
| 185 | if (r) | ||
| 186 | return r; | ||
| 187 | |||
| 188 | gpio_direction_input(OLPC_GPIO_ECSCI); | ||
| 189 | |||
| 190 | /* Clear pending EC SCI events */ | ||
| 191 | cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS); | ||
| 192 | cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_POSITIVE_EDGE_STS); | ||
| 193 | |||
| 194 | /* | ||
| 195 | * Enable EC SCI events, and map them to both a PME and the SCI | ||
| 196 | * interrupt. | ||
| 197 | * | ||
| 198 | * Ordinarily, in addition to functioning as GPIOs, Geode GPIOs can | ||
| 199 | * be mapped to regular interrupts *or* Geode-specific Power | ||
| 200 | * Management Events (PMEs) - events that bring the system out of | ||
| 201 | * suspend. In this case, we want both of those things - the system | ||
| 202 | * wakeup, *and* the ability to get an interrupt when an event occurs. | ||
| 203 | * | ||
| 204 | * To achieve this, we map the GPIO to a PME, and then we use one | ||
| 205 | * of the many generic knobs on the CS5535 PIC to additionally map the | ||
