0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/platform_device.h>
0011 #include <linux/device.h>
0012 #include <linux/module.h>
0013 #include <linux/of_gpio.h>
0014 #include <linux/of_irq.h>
0015 #include <linux/workqueue.h>
0016 #include <linux/reboot.h>
0017 #include <linux/interrupt.h>
0018
0019 #include <asm/machdep.h>
0020
0021 static struct device_node *halt_node;
0022
0023 static const struct of_device_id child_match[] = {
0024 {
0025 .compatible = "sgy,gpio-halt",
0026 },
0027 {},
0028 };
0029
0030 static void gpio_halt_wfn(struct work_struct *work)
0031 {
0032
0033 orderly_poweroff(true);
0034 }
0035 static DECLARE_WORK(gpio_halt_wq, gpio_halt_wfn);
0036
0037 static void __noreturn gpio_halt_cb(void)
0038 {
0039 enum of_gpio_flags flags;
0040 int trigger, gpio;
0041
0042 if (!halt_node)
0043 panic("No reset GPIO information was provided in DT\n");
0044
0045 gpio = of_get_gpio_flags(halt_node, 0, &flags);
0046
0047 if (!gpio_is_valid(gpio))
0048 panic("Provided GPIO is invalid\n");
0049
0050 trigger = (flags == OF_GPIO_ACTIVE_LOW);
0051
0052 printk(KERN_INFO "gpio-halt: triggering GPIO.\n");
0053
0054
0055 gpio_set_value(gpio, trigger);
0056
0057 panic("Halt failed\n");
0058 }
0059
0060
0061
0062 static irqreturn_t gpio_halt_irq(int irq, void *__data)
0063 {
0064 printk(KERN_INFO "gpio-halt: shutdown due to power button IRQ.\n");
0065 schedule_work(&gpio_halt_wq);
0066
0067 return IRQ_HANDLED;
0068 };
0069
0070 static int gpio_halt_probe(struct platform_device *pdev)
0071 {
0072 enum of_gpio_flags flags;
0073 struct device_node *node = pdev->dev.of_node;
0074 int gpio, err, irq;
0075 int trigger;
0076
0077 if (!node)
0078 return -ENODEV;
0079
0080
0081 halt_node = of_find_matching_node(node, child_match);
0082 if (!halt_node)
0083 return 0;
0084
0085
0086
0087 if (of_gpio_count(halt_node) != 1)
0088 return -EINVAL;
0089
0090
0091 gpio = of_get_gpio_flags(halt_node, 0, &flags);
0092 if (!gpio_is_valid(gpio))
0093 return -EINVAL;
0094
0095 err = gpio_request(gpio, "gpio-halt");
0096 if (err) {
0097 printk(KERN_ERR "gpio-halt: error requesting GPIO %d.\n",
0098 gpio);
0099 halt_node = NULL;
0100 return err;
0101 }
0102
0103 trigger = (flags == OF_GPIO_ACTIVE_LOW);
0104
0105 gpio_direction_output(gpio, !trigger);
0106
0107
0108 irq = irq_of_parse_and_map(halt_node, 0);
0109 err = request_irq(irq, gpio_halt_irq, IRQF_TRIGGER_RISING |
0110 IRQF_TRIGGER_FALLING, "gpio-halt", halt_node);
0111 if (err) {
0112 printk(KERN_ERR "gpio-halt: error requesting IRQ %d for "
0113 "GPIO %d.\n", irq, gpio);
0114 gpio_free(gpio);
0115 halt_node = NULL;
0116 return err;
0117 }
0118
0119
0120 ppc_md.halt = gpio_halt_cb;
0121 pm_power_off = gpio_halt_cb;
0122
0123 printk(KERN_INFO "gpio-halt: registered GPIO %d (%d trigger, %d"
0124 " irq).\n", gpio, trigger, irq);
0125
0126 return 0;
0127 }
0128
0129 static int gpio_halt_remove(struct platform_device *pdev)
0130 {
0131 if (halt_node) {
0132 int gpio = of_get_gpio(halt_node, 0);
0133 int irq = irq_of_parse_and_map(halt_node, 0);
0134
0135 free_irq(irq, halt_node);
0136
0137 ppc_md.halt = NULL;
0138 pm_power_off = NULL;
0139
0140 gpio_free(gpio);
0141
0142 halt_node = NULL;
0143 }
0144
0145 return 0;
0146 }
0147
0148 static const struct of_device_id gpio_halt_match[] = {
0149
0150
0151
0152 {
0153 .compatible = "fsl,qoriq-gpio",
0154 },
0155 {},
0156 };
0157 MODULE_DEVICE_TABLE(of, gpio_halt_match);
0158
0159 static struct platform_driver gpio_halt_driver = {
0160 .driver = {
0161 .name = "gpio-halt",
0162 .of_match_table = gpio_halt_match,
0163 },
0164 .probe = gpio_halt_probe,
0165 .remove = gpio_halt_remove,
0166 };
0167
0168 module_platform_driver(gpio_halt_driver);
0169
0170 MODULE_DESCRIPTION("Driver to support GPIO triggered system halt for Servergy CTS-1000 Systems.");
0171 MODULE_VERSION("1.0");
0172 MODULE_AUTHOR("Ben Collins <ben.c@servergy.com>");
0173 MODULE_LICENSE("GPL");