1 /* 2 * Support for OLPC XO-1 System Control Interrupts (SCI) 3 * 4 * Copyright (C) 2010 One Laptop per Child 5 * Copyright (C) 2006 Red Hat, Inc. 6 * Copyright (C) 2006 Advanced Micro Devices, Inc. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 */ 13 14 #include <linux/cs5535.h> 15 #include <linux/input.h> 16 #include <linux/interrupt.h> 17 #include <linux/platform_device.h> 18 #include <linux/pm.h> 19 #include <linux/mfd/core.h> 20 #include <linux/suspend.h> 21 22 #include <asm/io.h> 23 #include <asm/msr.h> 24 #include <asm/olpc.h> 25 26 #define DRV_NAME "olpc-xo1-sci" 27 #define PFX DRV_NAME ": " 28 29 static unsigned long acpi_base; 30 static struct input_dev *power_button_idev; 31 static int sci_irq; 32 33 static irqreturn_t xo1_sci_intr(int irq, void *dev_id) 34 { 35 struct platform_device *pdev = dev_id; 36 u32 sts; 37 u32 gpe; 38 39 sts = inl(acpi_base + CS5536_PM1_STS); 40 outl(sts | 0xffff, acpi_base + CS5536_PM1_STS); 41 42 gpe = inl(acpi_base + CS5536_PM_GPE0_STS); 43 outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); 44 45 dev_dbg(&pdev->dev, "sts %x gpe %x\n", sts, gpe); 46 47 if (sts & CS5536_PWRBTN_FLAG && !(sts & CS5536_WAK_FLAG)) { 48 input_report_key(power_button_idev, KEY_POWER, 1); 49 input_sync(power_button_idev); 50 input_report_key(power_button_idev, KEY_POWER, 0); 51 input_sync(power_button_idev); 52 } 53 54 return IRQ_HANDLED; 55 } 56 57 static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state) 58 { 59 if (device_may_wakeup(&power_button_idev->dev)) 60 olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN); 61 else 62 olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN); 63 return 0; 64 } 65 66 static int __devinit setup_sci_interrupt(struct platform_device *pdev) 67 { 68 u32 lo, hi; 69 u32 sts; 70 int r; 71 72 rdmsr(0x51400020, lo, hi); 73 sci_irq = (lo >> 20) & 15; 74 75 if (sci_irq) { 76 dev_info(&pdev->dev, "SCI is mapped to IRQ %d\n", sci_irq); 77 } else { 78 /* Zero means masked */ 79 dev_info(&pdev->dev, "SCI unmapped. Mapping to IRQ 3\n"); 80 sci_irq = 3; 81 lo |= 0x00300000; 82 wrmsrl(0x51400020, lo); 83 } 84 85 /* Select level triggered in PIC */ 86 if (sci_irq < 8) { 87 lo = inb(CS5536_PIC_INT_SEL1); 88 lo |= 1 << sci_irq; 89 outb(lo, CS5536_PIC_INT_SEL1); 90 } else { 91 lo = inb(CS5536_PIC_INT_SEL2); 92 lo |= 1 << (sci_irq - 8); 93 outb(lo, CS5536_PIC_INT_SEL2); 94 } 95 96 /* Enable SCI from power button, and clear pending interrupts */ 97 sts = inl(acpi_base + CS5536_PM1_STS); 98 outl((CS5536_PM_PWRBTN << 16) | 0xffff, acpi_base + CS5536_PM1_STS); 99 100 r = request_irq(sci_irq, xo1_sci_intr, 0, DRV_NAME, pdev); 101 if (r) 102 dev_err(&pdev->dev, "can't request interrupt\n"); 103 104 return r; 105 } 106 107 static int __devinit setup_power_button(struct platform_device *pdev) 108 { 109 int r; 110 111 power_button_idev = input_allocate_device(); 112 if (!power_button_idev) 113 return -ENOMEM; 114 115 power_button_idev->name = "Power Button"; 116 power_button_idev->phys = DRV_NAME "/input0"; 117 set_bit(EV_KEY, power_button_idev->evbit); 118 set_bit(KEY_POWER, power_button_idev->keybit); 119 120 power_button_idev->dev.parent = &pdev->dev; 121 device_init_wakeup(&power_button_idev->dev, 1); 122 123 r = input_register_device(power_button_idev); 124 if (r) { 125 dev_err(&pdev->dev, "failed to register power button: %d\n", r); 126 input_free_device(power_button_idev); 127 } 128 129 return r; 130 } 131 132 static void free_power_button(void) 133 { 134 input_unregister_device(power_button_idev); 135 input_free_device(power_button_idev); 136 } 137 138 static int __devinit xo1_sci_probe(struct platform_device *pdev) 139 { 140 struct resource *res; 141 int r; 142 143 /* don't run on non-XOs */ 144 if (!machine_is_olpc()) 145 return -ENODEV; 146 147 r = mfd_cell_enable(pdev); 148 if (r) 149 return r; 150 151 res = platform_get_resource(pdev, IORESOURCE_IO, 0); 152 if (!res) { 153 dev_err(&pdev->dev, "can't fetch device resource info\n"); 154 return -EIO; 155 } 156 acpi_base = res->start; 157 158 r = setup_power_button(pdev); 159 if (r) 160 return r; 161 162 r = setup_sci_interrupt(pdev); 163 if (r) 164 free_power_button(); 165 166 return r; 167 } 168 169 static int __devexit xo1_sci_remove(struct platform_device *pdev) 170 { 171 mfd_cell_disable(pdev); 172 free_irq(sci_irq, pdev); 173 free_power_button(); 174 acpi_base = 0; 175 return 0; 176 } 177 178 static struct platform_driver xo1_sci_driver = { 179 .driver = { 180 .name = "olpc-xo1-sci-acpi", 181 }, 182 .probe = xo1_sci_probe, 183 .remove = __devexit_p(xo1_sci_remove), 184 .suspend = xo1_sci_suspend, 185 }; 186 187 static int __init xo1_sci_init(void) 188 { 189 return platform_driver_register(&xo1_sci_driver); 190 } 191 arch_initcall(xo1_sci_init); 192