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