1 /* 2 * Servergy CTS-1000 Setup 3 * 4 * Maintained by Ben Collins <ben.c@servergy.com> 5 * 6 * Copyright 2012 by Servergy, Inc. 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms of the GNU General Public License as published by the 10 * Free Software Foundation; either version 2 of the License, or (at your 11 * option) any later version. 12 */ 13 14 #include <linux/platform_device.h> 15 #include <linux/device.h> 16 #include <linux/module.h> 17 #include <linux/of_gpio.h> 18 #include <linux/of_irq.h> 19 #include <linux/workqueue.h> 20 #include <linux/reboot.h> 21 #include <linux/interrupt.h> 22 23 #include <asm/machdep.h> 24 25 static struct device_node *halt_node; 26 27 static const struct of_device_id child_match[] = { 28 { 29 .compatible = "sgy,gpio-halt", 30 }, 31 {}, 32 }; 33 34 static void gpio_halt_wfn(struct work_struct *work) 35 { 36 /* Likely wont return */ 37 orderly_poweroff(true); 38 } 39 static DECLARE_WORK(gpio_halt_wq, gpio_halt_wfn); 40 41 static void __noreturn gpio_halt_cb(void) 42 { 43 enum of_gpio_flags flags; 44 int trigger, gpio; 45 46 if (!halt_node) 47 panic("No reset GPIO information was provided in DT\n"); 48 49 gpio = of_get_gpio_flags(halt_node, 0, &flags); 50 51 if (!gpio_is_valid(gpio)) 52 panic("Provided GPIO is invalid\n"); 53 54 trigger = (flags == OF_GPIO_ACTIVE_LOW); 55 56 printk(KERN_INFO "gpio-halt: triggering GPIO.\n"); 57 58 /* Probably wont return */ 59 gpio_set_value(gpio, trigger); 60 61 panic("Halt failed\n"); 62 } 63 64 /* This IRQ means someone pressed the power button and it is waiting for us 65 * to handle the shutdown/poweroff. */ 66 static irqreturn_t gpio_halt_irq(int irq, void *__data) 67 { 68 printk(KERN_INFO "gpio-halt: shutdown due to power button IRQ.\n"); 69 schedule_work(&gpio_halt_wq); 70 71 return IRQ_HANDLED; 72 }; 73 74 static int gpio_halt_probe(struct platform_device *pdev) 75 { 76 enum of_gpio_flags flags; 77 struct device_node *node = pdev->dev.of_node; 78 int gpio, err, irq; 79 int trigger; 80 81 if (!node) 82 return -ENODEV; 83 84 /* If there's no matching child, this isn't really an error */ 85 halt_node = of_find_matching_node(node, child_match); 86 if (!halt_node) 87 return 0; 88 89 /* Technically we could just read the first one, but punish 90 * DT writers for invalid form. */ 91 if (of_gpio_count(halt_node) != 1) 92 return -EINVAL; 93 94 /* Get the gpio number relative to the dynamic base. */ 95 gpio = of_get_gpio_flags(halt_node, 0, &flags); 96 if (!gpio_is_valid(gpio)) 97 return -EINVAL; 98 99 err = gpio_request(gpio, "gpio-halt"); 100 if (err) { 101 printk(KERN_ERR "gpio-halt: error requesting GPIO %d.\n", 102 gpio); 103 halt_node = NULL; 104 return err; 105 } 106 107 trigger = (flags == OF_GPIO_ACTIVE_LOW); 108 109 gpio_direction_output(gpio, !trigger); 110 111 /* Now get the IRQ which tells us when the power button is hit */ 112 irq = irq_of_parse_and_map(halt_node, 0); 113 err = request_irq(irq, gpio_halt_irq, IRQF_TRIGGER_RISING | 114 IRQF_TRIGGER_FALLING, "gpio-halt", halt_node); 115 if (err) { 116 printk(KERN_ERR "gpio-halt: error requesting IRQ %d for " 117 "GPIO %d.\n", irq, gpio); 118 gpio_free(gpio); 119 halt_node = NULL; 120 return err; 121 } 122 123 /* Register our halt function */ 124 ppc_md.halt = gpio_halt_cb; 125 pm_power_off = gpio_halt_cb; 126 127 printk(KERN_INFO "gpio-halt: registered GPIO %d (%d trigger, %d" 128 " irq).\n", gpio, trigger, irq); 129 130 return 0; 131 } 132 133 static int gpio_halt_remove(struct platform_device *pdev) 134 { 135 if (halt_node) { 136 int gpio = of_get_gpio(halt_node, 0); 137 int irq = irq_of_parse_and_map(halt_node, 0); 138 139 free_irq(irq, halt_node); 140 141 ppc_md.halt = NULL; 142 pm_power_off = NULL; 143 144 gpio_free(gpio); 145 146 halt_node = NULL; 147 } 148 149 return 0; 150 } 151 152 static const struct of_device_id gpio_halt_match[] = { 153 /* We match on the gpio bus itself and scan the children since they 154 * wont be matched against us. We know the bus wont match until it 155 * has been registered too. */ 156 { 157 .compatible = "fsl,qoriq-gpio", 158 }, 159 {}, 160 }; 161 MODULE_DEVICE_TABLE(of, gpio_halt_match); 162 163 static struct platform_driver gpio_halt_driver = { 164 .driver = { 165 .name = "gpio-halt", 166 .of_match_table = gpio_halt_match, 167 }, 168 .probe = gpio_halt_probe, 169 .remove = gpio_halt_remove, 170 }; 171 172 module_platform_driver(gpio_halt_driver); 173 174 MODULE_DESCRIPTION("Driver to support GPIO triggered system halt for Servergy CTS-1000 Systems."); 175 MODULE_VERSION("1.0"); 176 MODULE_AUTHOR("Ben Collins <ben.c@servergy.com>"); 177 MODULE_LICENSE("GPL"); 178