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