0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/cs5535.h>
0012 #include <linux/platform_device.h>
0013 #include <linux/export.h>
0014 #include <linux/pm.h>
0015 #include <linux/suspend.h>
0016 #include <linux/olpc-ec.h>
0017
0018 #include <asm/io.h>
0019 #include <asm/olpc.h>
0020
0021 #define DRV_NAME "olpc-xo1-pm"
0022
0023 static unsigned long acpi_base;
0024 static unsigned long pms_base;
0025
0026 static u16 wakeup_mask = CS5536_PM_PWRBTN;
0027
0028 static struct {
0029 unsigned long address;
0030 unsigned short segment;
0031 } ofw_bios_entry = { 0xF0000 + PAGE_OFFSET, __KERNEL_CS };
0032
0033
0034 void olpc_xo1_pm_wakeup_set(u16 value)
0035 {
0036 wakeup_mask |= value;
0037 }
0038 EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_set);
0039
0040
0041 void olpc_xo1_pm_wakeup_clear(u16 value)
0042 {
0043 wakeup_mask &= ~value;
0044 }
0045 EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);
0046
0047 static int xo1_power_state_enter(suspend_state_t pm_state)
0048 {
0049 unsigned long saved_sci_mask;
0050
0051
0052 if (pm_state != PM_SUSPEND_MEM)
0053 return -EINVAL;
0054
0055
0056
0057
0058
0059 saved_sci_mask = inl(acpi_base + CS5536_PM1_STS);
0060 saved_sci_mask &= 0xffff0000;
0061
0062
0063 do_olpc_suspend_lowlevel();
0064
0065
0066
0067
0068 outl(saved_sci_mask, acpi_base + CS5536_PM1_STS);
0069
0070 return 0;
0071 }
0072
0073 asmlinkage __visible int xo1_do_sleep(u8 sleep_state)
0074 {
0075 void *pgd_addr = __va(read_cr3_pa());
0076
0077
0078 outl(wakeup_mask << 16, acpi_base + CS5536_PM1_STS);
0079
0080 __asm__("movl %0,%%eax" : : "r" (pgd_addr));
0081 __asm__("call *(%%edi); cld"
0082 : : "D" (&ofw_bios_entry));
0083 __asm__("movb $0x34, %al\n\t"
0084 "outb %al, $0x70\n\t"
0085 "movb $0x30, %al\n\t"
0086 "outb %al, $0x71\n\t");
0087 return 0;
0088 }
0089
0090 static void xo1_power_off(void)
0091 {
0092 printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
0093
0094
0095 outl(0x40000000, pms_base + CS5536_PM_SCLK);
0096 outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL);
0097 outl(0x40000000, pms_base + CS5536_PM_WKXD);
0098 outl(0x40000000, pms_base + CS5536_PM_WKD);
0099
0100
0101 outl(0x0002ffff, pms_base + CS5536_PM_SSC);
0102 outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
0103
0104
0105 outl(0x00002000, acpi_base + CS5536_PM1_CNT);
0106 }
0107
0108 static int xo1_power_state_valid(suspend_state_t pm_state)
0109 {
0110
0111 return pm_state == PM_SUSPEND_MEM;
0112 }
0113
0114 static const struct platform_suspend_ops xo1_suspend_ops = {
0115 .valid = xo1_power_state_valid,
0116 .enter = xo1_power_state_enter,
0117 };
0118
0119 static int xo1_pm_probe(struct platform_device *pdev)
0120 {
0121 struct resource *res;
0122
0123
0124 if (!machine_is_olpc())
0125 return -ENODEV;
0126
0127 res = platform_get_resource(pdev, IORESOURCE_IO, 0);
0128 if (!res) {
0129 dev_err(&pdev->dev, "can't fetch device resource info\n");
0130 return -EIO;
0131 }
0132 if (strcmp(pdev->name, "cs5535-pms") == 0)
0133 pms_base = res->start;
0134 else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
0135 acpi_base = res->start;
0136
0137
0138 if (pms_base && acpi_base) {
0139 suspend_set_ops(&xo1_suspend_ops);
0140 pm_power_off = xo1_power_off;
0141 printk(KERN_INFO "OLPC XO-1 support registered\n");
0142 }
0143
0144 return 0;
0145 }
0146
0147 static int xo1_pm_remove(struct platform_device *pdev)
0148 {
0149 if (strcmp(pdev->name, "cs5535-pms") == 0)
0150 pms_base = 0;
0151 else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
0152 acpi_base = 0;
0153
0154 pm_power_off = NULL;
0155 return 0;
0156 }
0157
0158 static struct platform_driver cs5535_pms_driver = {
0159 .driver = {
0160 .name = "cs5535-pms",
0161 },
0162 .probe = xo1_pm_probe,
0163 .remove = xo1_pm_remove,
0164 };
0165
0166 static struct platform_driver cs5535_acpi_driver = {
0167 .driver = {
0168 .name = "olpc-xo1-pm-acpi",
0169 },
0170 .probe = xo1_pm_probe,
0171 .remove = xo1_pm_remove,
0172 };
0173
0174 static int __init xo1_pm_init(void)
0175 {
0176 int r;
0177
0178 r = platform_driver_register(&cs5535_pms_driver);
0179 if (r)
0180 return r;
0181
0182 r = platform_driver_register(&cs5535_acpi_driver);
0183 if (r)
0184 platform_driver_unregister(&cs5535_pms_driver);
0185
0186 return r;
0187 }
0188 arch_initcall(xo1_pm_init);