-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
powerpc: Add support for CTS-1000 GPIO controlled system poweroff
CTS-1000 is based on P4080. GPIO 27 is used to signal the FPGA to switch off power, and also associates IRQ 8 with front-panel button press (which we use to call orderly_poweroff()). The relevant device-tree looks like this: gpio0: gpio@130000 { compatible = "fsl,qoriq-gpio"; reg = <0x130000 0x1000>; interrupts = <55 2 0 0>; #gpio-cells = <2>; gpio-controller; /* Allows powering off the system via GPIO signal. */ gpio-halt@27 { compatible = "sgy,gpio-halt"; gpios = <&gpio0 27 0>; interrupts = <8 1 0 0>; }; }; Because the driver cannot match on sgy,gpio-halt (because the node is never processed through of_platform), it matches on fsl,qoriq-gpio and then checks child nodes for the matching sgy,gpio-halt. This also ensures that the GPIO controller is detected prior to sgy_cts1000's probe callback, since that node wont match via of_platform until the controller is registered. Also, because the GPIO handler for triggering system poweroff might sleep, the IRQ uses a workqueue to call orderly_poweroff(). As a final note, this driver may be expanded for other features specific to the CTS-1000. Signed-off-by: Ben Collins <ben.c@servergy.com> Cc: Jack Smith <jack.s@servergy.com> Cc: Vihar Rai <vihar.r@servergy.com> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
- Loading branch information
Benjamin Collins
authored and
Benjamin Herrenschmidt
committed
Jan 29, 2013
1 parent
34f364f
commit 5611fe4
Showing
3 changed files
with
185 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
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,176 @@ | ||
/* | ||
* Servergy CTS-1000 Setup | ||
* | ||
* Maintained by Ben Collins <ben.c@servergy.com> | ||
* | ||
* Copyright 2012 by Servergy, 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/platform_device.h> | ||
#include <linux/device.h> | ||
#include <linux/module.h> | ||
#include <linux/init.h> | ||
#include <linux/of_gpio.h> | ||
#include <linux/workqueue.h> | ||
#include <linux/reboot.h> | ||
#include <linux/interrupt.h> | ||
|
||
#include <asm/machdep.h> | ||
|
||
static struct device_node *halt_node; | ||
|
||
static struct of_device_id child_match[] = { | ||
{ | ||
.compatible = "sgy,gpio-halt", | ||
}, | ||
{}, | ||
}; | ||
|
||
static void gpio_halt_wfn(struct work_struct *work) | ||
{ | ||
/* Likely wont return */ | ||
orderly_poweroff(true); | ||
} | ||
static DECLARE_WORK(gpio_halt_wq, gpio_halt_wfn); | ||
|
||
static void gpio_halt_cb(void) | ||
{ | ||
enum of_gpio_flags flags; | ||
int trigger, gpio; | ||
|
||
if (!halt_node) | ||
return; | ||
|
||
gpio = of_get_gpio_flags(halt_node, 0, &flags); | ||
|
||
if (!gpio_is_valid(gpio)) | ||
return; | ||
|
||
trigger = (flags == OF_GPIO_ACTIVE_LOW); | ||
|
||
printk(KERN_INFO "gpio-halt: triggering GPIO.\n"); | ||
|
||
/* Probably wont return */ | ||
gpio_set_value(gpio, trigger); | ||
} | ||
|
||
/* This IRQ means someone pressed the power button and it is waiting for us | ||
* to handle the shutdown/poweroff. */ | ||
static irqreturn_t gpio_halt_irq(int irq, void *__data) | ||
{ | ||
printk(KERN_INFO "gpio-halt: shutdown due to power button IRQ.\n"); | ||
schedule_work(&gpio_halt_wq); | ||
|
||
return IRQ_HANDLED; | ||
}; | ||
|
||
static int __devinit gpio_halt_probe(struct platform_device *pdev) | ||
{ | ||
enum of_gpio_flags flags; | ||
struct device_node *node = pdev->dev.of_node; | ||
int gpio, err, irq; | ||
int trigger; | ||
|
||
if (!node) | ||
return -ENODEV; | ||
|
||
/* If there's no matching child, this isn't really an error */ | ||
halt_node = of_find_matching_node(node, child_match); | ||
if (!halt_node) | ||
return 0; | ||
|
||
/* Technically we could just read the first one, but punish | ||
* DT writers for invalid form. */ | ||
if (of_gpio_count(halt_node) != 1) | ||
return -EINVAL; | ||
|
||
/* Get the gpio number relative to the dynamic base. */ | ||
gpio = of_get_gpio_flags(halt_node, 0, &flags); | ||
if (!gpio_is_valid(gpio)) | ||
return -EINVAL; | ||
|
||
err = gpio_request(gpio, "gpio-halt"); | ||
if (err) { | ||
printk(KERN_ERR "gpio-halt: error requesting GPIO %d.\n", | ||
gpio); | ||
halt_node = NULL; | ||
return err; | ||
} | ||
|
||
trigger = (flags == OF_GPIO_ACTIVE_LOW); | ||
|
||
gpio_direction_output(gpio, !trigger); | ||
|
||
/* Now get the IRQ which tells us when the power button is hit */ | ||
irq = irq_of_parse_and_map(halt_node, 0); | ||
err = request_irq(irq, gpio_halt_irq, IRQF_TRIGGER_RISING | | ||
IRQF_TRIGGER_FALLING, "gpio-halt", halt_node); | ||
if (err) { | ||
printk(KERN_ERR "gpio-halt: error requesting IRQ %d for " | ||
"GPIO %d.\n", irq, gpio); | ||
gpio_free(gpio); | ||
halt_node = NULL; | ||
return err; | ||
} | ||
|
||
/* Register our halt function */ | ||
ppc_md.halt = gpio_halt_cb; | ||
ppc_md.power_off = gpio_halt_cb; | ||
|
||
printk(KERN_INFO "gpio-halt: registered GPIO %d (%d trigger, %d" | ||
" irq).\n", gpio, trigger, irq); | ||
|
||
return 0; | ||
} | ||
|
||
static int __devexit gpio_halt_remove(struct platform_device *pdev) | ||
{ | ||
if (halt_node) { | ||
int gpio = of_get_gpio(halt_node, 0); | ||
int irq = irq_of_parse_and_map(halt_node, 0); | ||
|
||
free_irq(irq, halt_node); | ||
|
||
ppc_md.halt = NULL; | ||
ppc_md.power_off = NULL; | ||
|
||
gpio_free(gpio); | ||
|
||
halt_node = NULL; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static struct of_device_id gpio_halt_match[] = { | ||
/* We match on the gpio bus itself and scan the children since they | ||
* wont be matched against us. We know the bus wont match until it | ||
* has been registered too. */ | ||
{ | ||
.compatible = "fsl,qoriq-gpio", | ||
}, | ||
{}, | ||
}; | ||
MODULE_DEVICE_TABLE(of, gpio_halt_match); | ||
|
||
static struct platform_driver gpio_halt_driver = { | ||
.driver = { | ||
.name = "gpio-halt", | ||
.owner = THIS_MODULE, | ||
.of_match_table = gpio_halt_match, | ||
}, | ||
.probe = gpio_halt_probe, | ||
.remove = __devexit_p(gpio_halt_remove), | ||
}; | ||
|
||
module_platform_driver(gpio_halt_driver); | ||
|
||
MODULE_DESCRIPTION("Driver to support GPIO triggered system halt for Servergy CTS-1000 Systems."); | ||
MODULE_VERSION("1.0"); | ||
MODULE_AUTHOR("Ben Collins <ben.c@servergy.com>"); | ||
MODULE_LICENSE("GPL"); |