linux/arch/x86/platform/olpc/olpc-xo1.c
<<
>>
Prefs
   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#include <linux/mfd/core.h>
  19
  20#include <asm/io.h>
  21#include <asm/olpc.h>
  22
  23#define DRV_NAME "olpc-xo1"
  24
  25/* PMC registers (PMS block) */
  26#define PM_SCLK         0x10
  27#define PM_IN_SLPCTL    0x20
  28#define PM_WKXD         0x34
  29#define PM_WKD          0x30
  30#define PM_SSC          0x54
  31
  32/* PM registers (ACPI block) */
  33#define PM1_CNT         0x08
  34#define PM_GPE0_STS     0x18
  35
  36static unsigned long acpi_base;
  37static unsigned long pms_base;
  38
  39static void xo1_power_off(void)
  40{
  41        printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
  42
  43        /* Enable all of these controls with 0 delay */
  44        outl(0x40000000, pms_base + PM_SCLK);
  45        outl(0x40000000, pms_base + PM_IN_SLPCTL);
  46        outl(0x40000000, pms_base + PM_WKXD);
  47        outl(0x40000000, pms_base + PM_WKD);
  48
  49        /* Clear status bits (possibly unnecessary) */
  50        outl(0x0002ffff, pms_base  + PM_SSC);
  51        outl(0xffffffff, acpi_base + PM_GPE0_STS);
  52
  53        /* Write SLP_EN bit to start the machinery */
  54        outl(0x00002000, acpi_base + PM1_CNT);
  55}
  56
  57static int __devinit olpc_xo1_probe(struct platform_device *pdev)
  58{
  59        struct resource *res;
  60        int err;
  61
  62        /* don't run on non-XOs */
  63        if (!machine_is_olpc())
  64                return -ENODEV;
  65
  66        err = mfd_cell_enable(pdev);
  67        if (err)
  68                return err;
  69
  70        res = platform_get_resource(pdev, IORESOURCE_IO, 0);
  71        if (!res) {
  72                dev_err(&pdev->dev, "can't fetch device resource info\n");
  73                return -EIO;
  74        }
  75        if (strcmp(pdev->name, "cs5535-pms") == 0)
  76                pms_base = res->start;
  77        else if (strcmp(pdev->name, "olpc-xo1-pm-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
  89static int __devexit olpc_xo1_remove(struct platform_device *pdev)
  90{
  91        mfd_cell_disable(pdev);
  92
  93        if (strcmp(pdev->name, "cs5535-pms") == 0)
  94                pms_base = 0;
  95        else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0)
  96                acpi_base = 0;
  97
  98        pm_power_off = NULL;
  99        return 0;
 100}
 101
 102static struct platform_driver cs5535_pms_drv = {
 103        .driver = {
 104                .name = "cs5535-pms",
 105                .owner = THIS_MODULE,
 106        },
 107        .probe = olpc_xo1_probe,
 108        .remove = __devexit_p(olpc_xo1_remove),
 109};
 110
 111static struct platform_driver cs5535_acpi_drv = {
 112        .driver = {
 113                .name = "olpc-xo1-pm-acpi",
 114                .owner = THIS_MODULE,
 115        },
 116        .probe = olpc_xo1_probe,
 117        .remove = __devexit_p(olpc_xo1_remove),
 118};
 119
 120static int __init olpc_xo1_init(void)
 121{
 122        int r;
 123
 124        r = platform_driver_register(&cs5535_pms_drv);
 125        if (r)
 126                return r;
 127
 128        r = platform_driver_register(&cs5535_acpi_drv);
 129        if (r)
 130                platform_driver_unregister(&cs5535_pms_drv);
 131
 132        return r;
 133}
 134
 135static void __exit olpc_xo1_exit(void)
 136{
 137        platform_driver_unregister(&cs5535_acpi_drv);
 138        platform_driver_unregister(&cs5535_pms_drv);
 139}
 140
 141MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
 142MODULE_LICENSE("GPL");
 143MODULE_ALIAS("platform:cs5535-pms");
 144
 145module_init(olpc_xo1_init);
 146module_exit(olpc_xo1_exit);
 147