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 gpio_halt_cb(void) 42 { 43 enum of_gpio_flags flags; 44 int trigger, gpio; 45 46 if (!halt_node) 47 return; 48 49 gpio = of_get_gpio_flags(halt_node, 0, &flags); 50 51 if (!gpio_is_valid(gpio)) 52 return; 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 62 /* This IRQ means someone pressed the power button and it is waiting for us 63 * to handle the shutdown/poweroff. */ 64 static irqreturn_t gpio_halt_irq(int irq, void *__data) 65 { 66 printk(KERN_INFO "gpio-halt: shutdown due to power button IRQ.\n"); 67 schedule_work(&gpio_halt_wq); 68 69 return IRQ_HANDLED; 70 }; 71 72 static int gpio_halt_probe(struct platform_device *pdev) 73 { 74 enum of_gpio_flags flags; 75 struct device_node *node = pdev->dev.of_node; 76 int gpio, err, irq; 77 int trigger; 78 79 if (!node) 80 return -ENODEV; 81 82 /* If there's no matching child, this isn't really an error */ 83 halt_node = of_find_matching_node(node, child_match); 84 if (!halt_node) 85 return 0; 86 87 /* Technically we could just read the first one, but punish 88 * DT writers for invalid form. */ 89 if (of_gpio_count(halt_node) != 1) 90 return -EINVAL; 91 92 /* Get the gpio number relative to the dynamic base. */ 93 gpio = of_get_gpio_flags(halt_node, 0, &flags); 94 if (!gpio_is_valid(gpio)) 95 return -EINVAL; 96 97 err = gpio_request(gpio, "gpio-halt"); 98 if (err) { 99 printk(KERN_ERR "gpio-halt: error requesting GPIO %d.\n", 100 gpio); 101 halt_node = NULL; 102 return err; 103 } 104 105 trigger = (flags == OF_GPIO_ACTIVE_LOW); 106 107 gpio_direction_output(gpio, !trigger); 108 109 /* Now get the IRQ which tells us when the power button is hit */ 110 irq = irq_of_parse_and_map(halt_node, 0); 111 err = request_irq(irq, gpio_halt_irq, IRQF_TRIGGER_RISING | 112 IRQF_TRIGGER_FALLING, "gpio-halt", halt_node); 113 if (err) { 114 printk(KERN_ERR "gpio-halt: error requesting IRQ %d for " 115 "GPIO %d.\n", irq, gpio); 116 gpio_free(gpio); 117 halt_node = NULL; 118 return err; 119 } 120 121 /* Register our halt function */ 122 ppc_md.halt = gpio_halt_cb; 123 pm_power_off = gpio_halt_cb; 124 125 printk(KERN_INFO "gpio-halt: registered GPIO %d (%d trigger, %d" 126 " irq).\n", gpio, trigger, irq); 127 128 return 0; 129 } 130 131 static int gpio_halt_remove(struct platform_device *pdev) 132 { 133 if (halt_node) { 134 int gpio = of_get_gpio(halt_node, 0); 135 int irq = irq_of_parse_and_map(halt_node, 0); 136 137 free_irq(irq, halt_node); 138 139 ppc_md.halt = NULL; 140 pm_power_off = NULL; 141 142 gpio_free(gpio); 143 144 halt_node = NULL; 145 } 146 147 return 0; 148 } 149 150 static const struct of_device_id gpio_halt_match[] = { 151 /* We match on the gpio bus itself and scan the children since they 152 * wont be matched against us. We know the bus wont match until it 153 * has been registered too. */ 154 { 155 .compatible = "fsl,qoriq-gpio", 156 }, 157 {}, 158 }; 159 MODULE_DEVICE_TABLE(of, gpio_halt_match); 160 161 static struct platform_driver gpio_halt_driver = { 162 .driver = { 163 .name = "gpio-halt", 164 .of_match_table = gpio_halt_match, 165 }, 166 .probe = gpio_halt_probe, 167 .remove = gpio_halt_remove, 168 }; 169 170 module_platform_driver(gpio_halt_driver); 171 172 MODULE_DESCRIPTION("Driver to support GPIO triggered system halt for Servergy CTS-1000 Systems."); 173 MODULE_VERSION("1.0"); 174 MODULE_AUTHOR("Ben Collins <ben.c@servergy.com>"); 175 MODULE_LICENSE("GPL"); 176