root/arch/x86/platform/olpc/olpc-xo1-pm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. olpc_xo1_pm_wakeup_set
  2. olpc_xo1_pm_wakeup_clear
  3. xo1_power_state_enter
  4. xo1_do_sleep
  5. xo1_power_off
  6. xo1_power_state_valid
  7. xo1_pm_probe
  8. xo1_pm_remove
  9. xo1_pm_init

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Support for power management features of the OLPC XO-1 laptop
   4  *
   5  * Copyright (C) 2010 Andres Salomon <dilinger@queued.net>
   6  * Copyright (C) 2010 One Laptop per Child
   7  * Copyright (C) 2006 Red Hat, Inc.
   8  * Copyright (C) 2006 Advanced Micro Devices, Inc.
   9  */
  10 
  11 #include <linux/cs5535.h>
  12 #include <linux/platform_device.h>
  13 #include <linux/export.h>
  14 #include <linux/pm.h>
  15 #include <linux/mfd/core.h>
  16 #include <linux/suspend.h>
  17 #include <linux/olpc-ec.h>
  18 
  19 #include <asm/io.h>
  20 #include <asm/olpc.h>
  21 
  22 #define DRV_NAME "olpc-xo1-pm"
  23 
  24 static unsigned long acpi_base;
  25 static unsigned long pms_base;
  26 
  27 static u16 wakeup_mask = CS5536_PM_PWRBTN;
  28 
  29 static struct {
  30         unsigned long address;
  31         unsigned short segment;
  32 } ofw_bios_entry = { 0xF0000 + PAGE_OFFSET, __KERNEL_CS };
  33 
  34 /* Set bits in the wakeup mask */
  35 void olpc_xo1_pm_wakeup_set(u16 value)
  36 {
  37         wakeup_mask |= value;
  38 }
  39 EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_set);
  40 
  41 /* Clear bits in the wakeup mask */
  42 void olpc_xo1_pm_wakeup_clear(u16 value)
  43 {
  44         wakeup_mask &= ~value;
  45 }
  46 EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear);
  47 
  48 static int xo1_power_state_enter(suspend_state_t pm_state)
  49 {
  50         unsigned long saved_sci_mask;
  51 
  52         /* Only STR is supported */
  53         if (pm_state != PM_SUSPEND_MEM)
  54                 return -EINVAL;
  55 
  56         /*
  57          * Save SCI mask (this gets lost since PM1_EN is used as a mask for
  58          * wakeup events, which is not necessarily the same event set)
  59          */
  60         saved_sci_mask = inl(acpi_base + CS5536_PM1_STS);
  61         saved_sci_mask &= 0xffff0000;
  62 
  63         /* Save CPU state */
  64         do_olpc_suspend_lowlevel();
  65 
  66         /* Resume path starts here */
  67 
  68         /* Restore SCI mask (using dword access to CS5536_PM1_EN) */
  69         outl(saved_sci_mask, acpi_base + CS5536_PM1_STS);
  70 
  71         return 0;
  72 }
  73 
  74 asmlinkage __visible int xo1_do_sleep(u8 sleep_state)
  75 {
  76         void *pgd_addr = __va(read_cr3_pa());
  77 
  78         /* Program wakeup mask (using dword access to CS5536_PM1_EN) */
  79         outl(wakeup_mask << 16, acpi_base + CS5536_PM1_STS);
  80 
  81         __asm__("movl %0,%%eax" : : "r" (pgd_addr));
  82         __asm__("call *(%%edi); cld"
  83                 : : "D" (&ofw_bios_entry));
  84         __asm__("movb $0x34, %al\n\t"
  85                 "outb %al, $0x70\n\t"
  86                 "movb $0x30, %al\n\t"
  87                 "outb %al, $0x71\n\t");
  88         return 0;
  89 }
  90 
  91 static void xo1_power_off(void)
  92 {
  93         printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
  94 
  95         /* Enable all of these controls with 0 delay */
  96         outl(0x40000000, pms_base + CS5536_PM_SCLK);
  97         outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL);
  98         outl(0x40000000, pms_base + CS5536_PM_WKXD);
  99         outl(0x40000000, pms_base + CS5536_PM_WKD);
 100 
 101         /* Clear status bits (possibly unnecessary) */
 102         outl(0x0002ffff, pms_base  + CS5536_PM_SSC);
 103         outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS);
 104 
 105         /* Write SLP_EN bit to start the machinery */
 106         outl(0x00002000, acpi_base + CS5536_PM1_CNT);
 107 }
 108 
 109 static int xo1_power_state_valid(suspend_state_t pm_state)
 110 {
 111         /* suspend-to-RAM only */
 112         return pm_state == PM_SUSPEND_MEM;
 113 }
 114 
 115 static const struct platform_suspend_ops xo1_suspend_ops = {
 116         .valid = xo1_power_state_valid,
 117         .enter = xo1_power_state_enter,
 118 };
 119 
 120 static int xo1_pm_probe(struct platform_device *pdev)
 121 {
 122         struct resource *res;
 123         int err;
 124 
 125         /* don't run on non-XOs */
 126         if (!machine_is_olpc())
 127                 return -ENODEV;
 128 
 129         err = mfd_cell_enable(pdev);
 130         if (err)
 131                 return err;
 132 
 133         res = platform_get_resource(pdev, IORESOURCE_IO, 0);
 134         if (!res) {
 135                 dev_err(&pdev->dev, "can't fetch device resource info\n");
 136                 return -EIO;
 137         }
 138         if (strcmp(pdev->name, "cs5535-pms") == 0)
 139                 pms_base = res->start;
 140         else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
 141                 acpi_base = res->start;
 142 
 143         /* If we have both addresses, we can override the poweroff hook */
 144         if (pms_base && acpi_base) {
 145                 suspend_set_ops(&xo1_suspend_ops);
 146                 pm_power_off = xo1_power_off;
 147                 printk(KERN_INFO "OLPC XO-1 support registered\n");
 148         }
 149 
 150         return 0;
 151 }
 152 
 153 static int xo1_pm_remove(struct platform_device *pdev)
 154 {
 155         mfd_cell_disable(pdev);
 156 
 157         if (strcmp(pdev->name, "cs5535-pms") == 0)
 158                 pms_base = 0;
 159         else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
 160                 acpi_base = 0;
 161 
 162         pm_power_off = NULL;
 163         return 0;
 164 }
 165 
 166 static struct platform_driver cs5535_pms_driver = {
 167         .driver = {
 168                 .name = "cs5535-pms",
 169         },
 170         .probe = xo1_pm_probe,
 171         .remove = xo1_pm_remove,
 172 };
 173 
 174 static struct platform_driver cs5535_acpi_driver = {
 175         .driver = {
 176                 .name = "olpc-xo1-pm-acpi",
 177         },
 178         .probe = xo1_pm_probe,
 179         .remove = xo1_pm_remove,
 180 };
 181 
 182 static int __init xo1_pm_init(void)
 183 {
 184         int r;
 185 
 186         r = platform_driver_register(&cs5535_pms_driver);
 187         if (r)
 188                 return r;
 189 
 190         r = platform_driver_register(&cs5535_acpi_driver);
 191         if (r)
 192                 platform_driver_unregister(&cs5535_pms_driver);
 193 
 194         return r;
 195 }
 196 arch_initcall(xo1_pm_init);

/* [<][>][^][v][top][bottom][index][help] */