-
Notifications
You must be signed in to change notification settings - Fork 53.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
x86, olpc: Add XO-1 SCI driver and power button control
The System Control Interrupt is used in the OLPC XO-1 to control various features of the laptop. Add the driver base and the power button functionality. This driver can't be built as a module, because functionality added in future patches means that some drivers need to know at boot-time whether SCI-based functionality is available. Signed-off-by: Daniel Drake <dsd@laptop.org> Link: http://lkml.kernel.org/r/1309019658-1712-6-git-send-email-dsd@laptop.org Acked-by: Andres Salomon <dilinger@queued.net> Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
- Loading branch information
Daniel Drake
authored and
H. Peter Anvin
committed
Jul 6, 2011
1 parent
97c4cb7
commit 7feda8e
Showing
4 changed files
with
209 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o | ||
obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o | ||
obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
/* | ||
* Support for OLPC XO-1 System Control Interrupts (SCI) | ||
* | ||
* Copyright (C) 2010 One Laptop per Child | ||
* Copyright (C) 2006 Red Hat, Inc. | ||
* Copyright (C) 2006 Advanced Micro Devices, Inc. | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
*/ | ||
|
||
#include <linux/cs5535.h> | ||
#include <linux/input.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/platform_device.h> | ||
#include <linux/pm.h> | ||
#include <linux/mfd/core.h> | ||
#include <linux/suspend.h> | ||
|
||
#include <asm/io.h> | ||
#include <asm/msr.h> | ||
#include <asm/olpc.h> | ||
|
||
#define DRV_NAME "olpc-xo1-sci" | ||
#define PFX DRV_NAME ": " | ||
|
||
static unsigned long acpi_base; | ||
static struct input_dev *power_button_idev; | ||
static int sci_irq; | ||
|
||
static irqreturn_t xo1_sci_intr(int irq, void *dev_id) | ||
{ | ||
struct platform_device *pdev = dev_id; | ||
u32 sts; | ||
u32 gpe; | ||
|
||
sts = inl(acpi_base + CS5536_PM1_STS); | ||
outl(sts | 0xffff, acpi_base + CS5536_PM1_STS); | ||
|
||
gpe = inl(acpi_base + CS5536_PM_GPE0_STS); | ||
outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); | ||
|
||
dev_dbg(&pdev->dev, "sts %x gpe %x\n", sts, gpe); | ||
|
||
if (sts & CS5536_PWRBTN_FLAG && !(sts & CS5536_WAK_FLAG)) { | ||
input_report_key(power_button_idev, KEY_POWER, 1); | ||
input_sync(power_button_idev); | ||
input_report_key(power_button_idev, KEY_POWER, 0); | ||
input_sync(power_button_idev); | ||
} | ||
|
||
return IRQ_HANDLED; | ||
} | ||
|
||
static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state) | ||
{ | ||
if (device_may_wakeup(&power_button_idev->dev)) | ||
olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN); | ||
else | ||
olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN); | ||
return 0; | ||
} | ||
|
||
static int __devinit setup_sci_interrupt(struct platform_device *pdev) | ||
{ | ||
u32 lo, hi; | ||
u32 sts; | ||
int r; | ||
|
||
rdmsr(0x51400020, lo, hi); | ||
sci_irq = (lo >> 20) & 15; | ||
|
||
if (sci_irq) { | ||
dev_info(&pdev->dev, "SCI is mapped to IRQ %d\n", sci_irq); | ||
} else { | ||
/* Zero means masked */ | ||
dev_info(&pdev->dev, "SCI unmapped. Mapping to IRQ 3\n"); | ||
sci_irq = 3; | ||
lo |= 0x00300000; | ||
wrmsrl(0x51400020, lo); | ||
} | ||
|
||
/* Select level triggered in PIC */ | ||
if (sci_irq < 8) { | ||
lo = inb(CS5536_PIC_INT_SEL1); | ||
lo |= 1 << sci_irq; | ||
outb(lo, CS5536_PIC_INT_SEL1); | ||
} else { | ||
lo = inb(CS5536_PIC_INT_SEL2); | ||
lo |= 1 << (sci_irq - 8); | ||
outb(lo, CS5536_PIC_INT_SEL2); | ||
} | ||
|
||
/* Enable SCI from power button, and clear pending interrupts */ | ||
sts = inl(acpi_base + CS5536_PM1_STS); | ||
outl((CS5536_PM_PWRBTN << 16) | 0xffff, acpi_base + CS5536_PM1_STS); | ||
|
||
r = request_irq(sci_irq, xo1_sci_intr, 0, DRV_NAME, pdev); | ||
if (r) | ||
dev_err(&pdev->dev, "can't request interrupt\n"); | ||
|
||
return r; | ||
} | ||
|
||
static int __devinit setup_power_button(struct platform_device *pdev) | ||
{ | ||
int r; | ||
|
||
power_button_idev = input_allocate_device(); | ||
if (!power_button_idev) | ||
return -ENOMEM; | ||
|
||
power_button_idev->name = "Power Button"; | ||
power_button_idev->phys = DRV_NAME "/input0"; | ||
set_bit(EV_KEY, power_button_idev->evbit); | ||
set_bit(KEY_POWER, power_button_idev->keybit); | ||
|
||
power_button_idev->dev.parent = &pdev->dev; | ||
device_init_wakeup(&power_button_idev->dev, 1); | ||
|
||
r = input_register_device(power_button_idev); | ||
if (r) { | ||
dev_err(&pdev->dev, "failed to register power button: %d\n", r); | ||
input_free_device(power_button_idev); | ||
} | ||
|
||
return r; | ||
} | ||
|
||
static void free_power_button(void) | ||
{ | ||
input_unregister_device(power_button_idev); | ||
input_free_device(power_button_idev); | ||
} | ||
|
||
static int __devinit xo1_sci_probe(struct platform_device *pdev) | ||
{ | ||
struct resource *res; | ||
int r; | ||
|
||
/* don't run on non-XOs */ | ||
if (!machine_is_olpc()) | ||
return -ENODEV; | ||
|
||
r = mfd_cell_enable(pdev); | ||
if (r) | ||
return r; | ||
|
||
res = platform_get_resource(pdev, IORESOURCE_IO, 0); | ||
if (!res) { | ||
dev_err(&pdev->dev, "can't fetch device resource info\n"); | ||
return -EIO; | ||
} | ||
acpi_base = res->start; | ||
|
||
r = setup_power_button(pdev); | ||
if (r) | ||
return r; | ||
|
||
r = setup_sci_interrupt(pdev); | ||
if (r) | ||
free_power_button(); | ||
|
||
return r; | ||
} | ||
|
||
static int __devexit xo1_sci_remove(struct platform_device *pdev) | ||
{ | ||
mfd_cell_disable(pdev); | ||
free_irq(sci_irq, pdev); | ||
free_power_button(); | ||
acpi_base = 0; | ||
return 0; | ||
} | ||
|
||
static struct platform_driver xo1_sci_driver = { | ||
.driver = { | ||
.name = "olpc-xo1-sci-acpi", | ||
}, | ||
.probe = xo1_sci_probe, | ||
.remove = __devexit_p(xo1_sci_remove), | ||
.suspend = xo1_sci_suspend, | ||
}; | ||
|
||
static int __init xo1_sci_init(void) | ||
{ | ||
return platform_driver_register(&xo1_sci_driver); | ||
} | ||
arch_initcall(xo1_sci_init); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters