xref: /linux/arch/x86/platform/olpc/olpc-xo1-pm.c (revision a3128588b3c6be634a9013a375903e0b55668f0a)
1*a3128588SDaniel Drake /*
2*a3128588SDaniel Drake  * Support for power management features of the OLPC XO-1 laptop
3*a3128588SDaniel Drake  *
4*a3128588SDaniel Drake  * Copyright (C) 2010 Andres Salomon <dilinger@queued.net>
5*a3128588SDaniel Drake  * Copyright (C) 2010 One Laptop per Child
6*a3128588SDaniel Drake  * Copyright (C) 2006 Red Hat, Inc.
7*a3128588SDaniel Drake  * Copyright (C) 2006 Advanced Micro Devices, Inc.
8*a3128588SDaniel Drake  *
9*a3128588SDaniel Drake  * This program is free software; you can redistribute it and/or modify
10*a3128588SDaniel Drake  * it under the terms of the GNU General Public License as published by
11*a3128588SDaniel Drake  * the Free Software Foundation; either version 2 of the License, or
12*a3128588SDaniel Drake  * (at your option) any later version.
13*a3128588SDaniel Drake  */
14*a3128588SDaniel Drake 
15*a3128588SDaniel Drake #include <linux/cs5535.h>
16*a3128588SDaniel Drake #include <linux/platform_device.h>
17*a3128588SDaniel Drake #include <linux/pm.h>
18*a3128588SDaniel Drake #include <linux/mfd/core.h>
19*a3128588SDaniel Drake 
20*a3128588SDaniel Drake #include <asm/io.h>
21*a3128588SDaniel Drake #include <asm/olpc.h>
22*a3128588SDaniel Drake 
23*a3128588SDaniel Drake #define DRV_NAME "olpc-xo1-pm"
24*a3128588SDaniel Drake 
25*a3128588SDaniel Drake static unsigned long acpi_base;
26*a3128588SDaniel Drake static unsigned long pms_base;
27*a3128588SDaniel Drake 
28*a3128588SDaniel Drake static void xo1_power_off(void)
29*a3128588SDaniel Drake {
30*a3128588SDaniel Drake 	printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
31*a3128588SDaniel Drake 
32*a3128588SDaniel Drake 	/* Enable all of these controls with 0 delay */
33*a3128588SDaniel Drake 	outl(0x40000000, pms_base + CS5536_PM_SCLK);
34*a3128588SDaniel Drake 	outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL);
35*a3128588SDaniel Drake 	outl(0x40000000, pms_base + CS5536_PM_WKXD);
36*a3128588SDaniel Drake 	outl(0x40000000, pms_base + CS5536_PM_WKD);
37*a3128588SDaniel Drake 
38*a3128588SDaniel Drake 	/* Clear status bits (possibly unnecessary) */
39*a3128588SDaniel Drake 	outl(0x0002ffff, pms_base  + CS5536_PM_SSC);
40*a3128588SDaniel Drake 	outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
41*a3128588SDaniel Drake 
42*a3128588SDaniel Drake 	/* Write SLP_EN bit to start the machinery */
43*a3128588SDaniel Drake 	outl(0x00002000, acpi_base + CS5536_PM1_CNT);
44*a3128588SDaniel Drake }
45*a3128588SDaniel Drake 
46*a3128588SDaniel Drake static int __devinit xo1_pm_probe(struct platform_device *pdev)
47*a3128588SDaniel Drake {
48*a3128588SDaniel Drake 	struct resource *res;
49*a3128588SDaniel Drake 	int err;
50*a3128588SDaniel Drake 
51*a3128588SDaniel Drake 	/* don't run on non-XOs */
52*a3128588SDaniel Drake 	if (!machine_is_olpc())
53*a3128588SDaniel Drake 		return -ENODEV;
54*a3128588SDaniel Drake 
55*a3128588SDaniel Drake 	err = mfd_cell_enable(pdev);
56*a3128588SDaniel Drake 	if (err)
57*a3128588SDaniel Drake 		return err;
58*a3128588SDaniel Drake 
59*a3128588SDaniel Drake 	res = platform_get_resource(pdev, IORESOURCE_IO, 0);
60*a3128588SDaniel Drake 	if (!res) {
61*a3128588SDaniel Drake 		dev_err(&pdev->dev, "can't fetch device resource info\n");
62*a3128588SDaniel Drake 		return -EIO;
63*a3128588SDaniel Drake 	}
64*a3128588SDaniel Drake 	if (strcmp(pdev->name, "cs5535-pms") == 0)
65*a3128588SDaniel Drake 		pms_base = res->start;
66*a3128588SDaniel Drake 	else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
67*a3128588SDaniel Drake 		acpi_base = res->start;
68*a3128588SDaniel Drake 
69*a3128588SDaniel Drake 	/* If we have both addresses, we can override the poweroff hook */
70*a3128588SDaniel Drake 	if (pms_base && acpi_base) {
71*a3128588SDaniel Drake 		pm_power_off = xo1_power_off;
72*a3128588SDaniel Drake 		printk(KERN_INFO "OLPC XO-1 support registered\n");
73*a3128588SDaniel Drake 	}
74*a3128588SDaniel Drake 
75*a3128588SDaniel Drake 	return 0;
76*a3128588SDaniel Drake }
77*a3128588SDaniel Drake 
78*a3128588SDaniel Drake static int __devexit xo1_pm_remove(struct platform_device *pdev)
79*a3128588SDaniel Drake {
80*a3128588SDaniel Drake 	mfd_cell_disable(pdev);
81*a3128588SDaniel Drake 
82*a3128588SDaniel Drake 	if (strcmp(pdev->name, "cs5535-pms") == 0)
83*a3128588SDaniel Drake 		pms_base = 0;
84*a3128588SDaniel Drake 	else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
85*a3128588SDaniel Drake 		acpi_base = 0;
86*a3128588SDaniel Drake 
87*a3128588SDaniel Drake 	pm_power_off = NULL;
88*a3128588SDaniel Drake 	return 0;
89*a3128588SDaniel Drake }
90*a3128588SDaniel Drake 
91*a3128588SDaniel Drake static struct platform_driver cs5535_pms_driver = {
92*a3128588SDaniel Drake 	.driver = {
93*a3128588SDaniel Drake 		.name = "cs5535-pms",
94*a3128588SDaniel Drake 		.owner = THIS_MODULE,
95*a3128588SDaniel Drake 	},
96*a3128588SDaniel Drake 	.probe = xo1_pm_probe,
97*a3128588SDaniel Drake 	.remove = __devexit_p(xo1_pm_remove),
98*a3128588SDaniel Drake };
99*a3128588SDaniel Drake 
100*a3128588SDaniel Drake static struct platform_driver cs5535_acpi_driver = {
101*a3128588SDaniel Drake 	.driver = {
102*a3128588SDaniel Drake 		.name = "olpc-xo1-pm-acpi",
103*a3128588SDaniel Drake 		.owner = THIS_MODULE,
104*a3128588SDaniel Drake 	},
105*a3128588SDaniel Drake 	.probe = xo1_pm_probe,
106*a3128588SDaniel Drake 	.remove = __devexit_p(xo1_pm_remove),
107*a3128588SDaniel Drake };
108*a3128588SDaniel Drake 
109*a3128588SDaniel Drake static int __init xo1_pm_init(void)
110*a3128588SDaniel Drake {
111*a3128588SDaniel Drake 	int r;
112*a3128588SDaniel Drake 
113*a3128588SDaniel Drake 	r = platform_driver_register(&cs5535_pms_driver);
114*a3128588SDaniel Drake 	if (r)
115*a3128588SDaniel Drake 		return r;
116*a3128588SDaniel Drake 
117*a3128588SDaniel Drake 	r = platform_driver_register(&cs5535_acpi_driver);
118*a3128588SDaniel Drake 	if (r)
119*a3128588SDaniel Drake 		platform_driver_unregister(&cs5535_pms_driver);
120*a3128588SDaniel Drake 
121*a3128588SDaniel Drake 	return r;
122*a3128588SDaniel Drake }
123*a3128588SDaniel Drake arch_initcall(xo1_pm_init);
124