Merge branch 'devel-stable' of master.kernel.org:/home/rmk/linux-2.6-arm
[pandora-kernel.git] / arch / x86 / platform / olpc / olpc-xo1.c
1 /*
2  * Support for features of the OLPC XO-1 laptop
3  *
4  * Copyright (C) 2010 Andres Salomon <dilinger@queued.net>
5  * Copyright (C) 2010 One Laptop per Child
6  * Copyright (C) 2006 Red Hat, Inc.
7  * Copyright (C) 2006 Advanced Micro Devices, Inc.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  */
14
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/pm.h>
18
19 #include <asm/io.h>
20 #include <asm/olpc.h>
21
22 #define DRV_NAME "olpc-xo1"
23
24 /* PMC registers (PMS block) */
25 #define PM_SCLK         0x10
26 #define PM_IN_SLPCTL    0x20
27 #define PM_WKXD         0x34
28 #define PM_WKD          0x30
29 #define PM_SSC          0x54
30
31 /* PM registers (ACPI block) */
32 #define PM1_CNT         0x08
33 #define PM_GPE0_STS     0x18
34
35 static unsigned long acpi_base;
36 static unsigned long pms_base;
37
38 static void xo1_power_off(void)
39 {
40         printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
41
42         /* Enable all of these controls with 0 delay */
43         outl(0x40000000, pms_base + PM_SCLK);
44         outl(0x40000000, pms_base + PM_IN_SLPCTL);
45         outl(0x40000000, pms_base + PM_WKXD);
46         outl(0x40000000, pms_base + PM_WKD);
47
48         /* Clear status bits (possibly unnecessary) */
49         outl(0x0002ffff, pms_base  + PM_SSC);
50         outl(0xffffffff, acpi_base + PM_GPE0_STS);
51
52         /* Write SLP_EN bit to start the machinery */
53         outl(0x00002000, acpi_base + PM1_CNT);
54 }
55
56 static int __devinit olpc_xo1_probe(struct platform_device *pdev)
57 {
58         struct resource *res;
59
60         /* don't run on non-XOs */
61         if (!machine_is_olpc())
62                 return -ENODEV;
63
64         res = platform_get_resource(pdev, IORESOURCE_IO, 0);
65         if (!res) {
66                 dev_err(&pdev->dev, "can't fetch device resource info\n");
67                 return -EIO;
68         }
69
70         if (!request_region(res->start, resource_size(res), DRV_NAME)) {
71                 dev_err(&pdev->dev, "can't request region\n");
72                 return -EIO;
73         }
74
75         if (strcmp(pdev->name, "cs5535-pms") == 0)
76                 pms_base = res->start;
77         else if (strcmp(pdev->name, "cs5535-acpi") == 0)
78                 acpi_base = res->start;
79
80         /* If we have both addresses, we can override the poweroff hook */
81         if (pms_base && acpi_base) {
82                 pm_power_off = xo1_power_off;
83                 printk(KERN_INFO "OLPC XO-1 support registered\n");
84         }
85
86         return 0;
87 }
88
89 static int __devexit olpc_xo1_remove(struct platform_device *pdev)
90 {
91         struct resource *r;
92
93         r = platform_get_resource(pdev, IORESOURCE_IO, 0);
94         release_region(r->start, resource_size(r));
95
96         if (strcmp(pdev->name, "cs5535-pms") == 0)
97                 pms_base = 0;
98         else if (strcmp(pdev->name, "cs5535-acpi") == 0)
99                 acpi_base = 0;
100
101         pm_power_off = NULL;
102         return 0;
103 }
104
105 static struct platform_driver cs5535_pms_drv = {
106         .driver = {
107                 .name = "cs5535-pms",
108                 .owner = THIS_MODULE,
109         },
110         .probe = olpc_xo1_probe,
111         .remove = __devexit_p(olpc_xo1_remove),
112 };
113
114 static struct platform_driver cs5535_acpi_drv = {
115         .driver = {
116                 .name = "cs5535-acpi",
117                 .owner = THIS_MODULE,
118         },
119         .probe = olpc_xo1_probe,
120         .remove = __devexit_p(olpc_xo1_remove),
121 };
122
123 static int __init olpc_xo1_init(void)
124 {
125         int r;
126
127         r = platform_driver_register(&cs5535_pms_drv);
128         if (r)
129                 return r;
130
131         r = platform_driver_register(&cs5535_acpi_drv);
132         if (r)
133                 platform_driver_unregister(&cs5535_pms_drv);
134
135         return r;
136 }
137
138 static void __exit olpc_xo1_exit(void)
139 {
140         platform_driver_unregister(&cs5535_acpi_drv);
141         platform_driver_unregister(&cs5535_pms_drv);
142 }
143
144 MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
145 MODULE_LICENSE("GPL");
146 MODULE_ALIAS("platform:olpc-xo1");
147
148 module_init(olpc_xo1_init);
149 module_exit(olpc_xo1_exit);