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/init.h> 18 #include <linux/of_gpio.h> 19 #include <linux/of_irq.h> 20 #include <linux/workqueue.h> 21 #include <linux/reboot.h> 22 #include <linux/interrupt.h> 23 24 #include <asm/machdep.h> 25 26 static struct device_node *halt_node; 27 28 static struct of_device_id child_match[] = { 29 { 30 .compatible = "sgy,gpio-halt", 31 }, 32 {}, 33 }; 34 35 static void gpio_halt_wfn(struct work_struct *work) 36 { 37 /* Likely wont return */ 38 orderly_poweroff(true); 39 } 40 static DECLARE_WORK(gpio_halt_wq, gpio_halt_wfn); 41 42 static void gpio_halt_cb(void) 43 { 44 enum of_gpio_flags flags; 45 int trigger, gpio; 46 47 if (!halt_node) 48 return; 49 50 gpio = of_get_gpio_flags(halt_node, 0, &flags); 51 52 if (!gpio_is_valid(gpio)) 53 return; 54 55 trigger = (flags == OF_GPIO_ACTIVE_LOW); 56 57 printk(KERN_INFO "gpio-halt: triggering GPIO.\n"); 58 59 /* Probably wont return */ 60 gpio_set_value(gpio, trigger); 61 } 62 63 /* This IRQ means someone pressed the power button and it is waiting for us 64 * to handle the shutdown/poweroff. */ 65 static irqreturn_t gpio_halt_irq(int irq, void *__data) 66 { 67 printk(KERN_INFO "gpio-halt: shutdown due to power button IRQ.\n"); 68 schedule_work(&gpio_halt_wq); 69 70 return IRQ_HANDLED; 71 }; 72 73 static int gpio_halt_probe(struct platform_device *pdev) 74 { 75 enum of_gpio_flags flags; 76 struct device_node *node = pdev->dev.of_node; 77 int gpio, err, irq; 78 int trigger; 79 80 if (!node) 81 return -ENODEV; 82 83 /* If there's no matching child, this isn't really an error */ 84 halt_node = of_find_matching_node(node, child_match); 85 if (!halt_node) 86 return 0; 87 88 /* Technically we could just read the first one, but punish 89 * DT writers for invalid form. */ 90 if (of_gpio_count(halt_node) != 1) 91 return -EINVAL; 92 93 /* Get the gpio number relative to the dynamic base. */ 94 gpio = of_get_gpio_flags(halt_node, 0, &flags); 95 if (!gpio_is_valid(gpio)) 96 return -EINVAL; 97 98 err = gpio_request(gpio, "gpio-halt"); 99 if (err) { 100 printk(KERN_ERR "gpio-halt: error requesting GPIO %d.\n", 101 gpio); 102 halt_node = NULL; 103 return err; 104 } 105 106 trigger = (flags == OF_GPIO_ACTIVE_LOW); 107 108 gpio_direction_output(gpio, !trigger); 109 110 /* Now get the IRQ which tells us when the power button is hit */ 111 irq = irq_of_parse_and_map(halt_node, 0); 112 err = request_irq(irq, gpio_halt_irq, IRQF_TRIGGER_RISING | 113 IRQF_TRIGGER_FALLING, "gpio-halt", halt_node); 114 if (err) { 115 printk(KERN_ERR "gpio-halt: error requesting IRQ %d for " 116 "GPIO %d.\n", irq, gpio); 117 gpio_free(gpio); 118 halt_node = NULL; 119 return err; 120 } 121 122 /* Register our halt function */ 123 ppc_md.halt = gpio_halt_cb; 124 ppc_md.power_off = gpio_halt_cb; 125 126 printk(KERN_INFO "gpio-halt: registered GPIO %d (%d trigger, %d" 127 " irq).\n", gpio, trigger, irq); 128 129 return 0; 130 } 131 132 static int gpio_halt_remove(struct platform_device *pdev) 133 { 134 if (halt_node) { 135 int gpio = of_get_gpio(halt_node, 0); 136 int irq = irq_of_parse_and_map(halt_node, 0); 137 138 free_irq(irq, halt_node); 139 140 ppc_md.halt = NULL; 141 ppc_md.power_off = NULL; 142 143 gpio_free(gpio); 144 145 halt_node = NULL; 146 } 147 148 return 0; 149 } 150 151 static struct of_device_id gpio_halt_match[] = { 152 /* We match on the gpio bus itself and scan the children since they 153 * wont be matched against us. We know the bus wont match until it 154 * has been registered too. */ 155 { 156 .compatible = "fsl,qoriq-gpio", 157 }, 158 {}, 159 }; 160 MODULE_DEVICE_TABLE(of, gpio_halt_match); 161 162 static struct platform_driver gpio_halt_driver = { 163 .driver = { 164 .name = "gpio-halt", 165 .owner = THIS_MODULE, 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