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 #define pr_fmt(fmt) "gpio-halt: " fmt 11 12 #include <linux/err.h> 13 #include <linux/platform_device.h> 14 #include <linux/device.h> 15 #include <linux/gpio/consumer.h> 16 #include <linux/module.h> 17 #include <linux/of_irq.h> 18 #include <linux/workqueue.h> 19 #include <linux/reboot.h> 20 #include <linux/interrupt.h> 21 22 #include <asm/machdep.h> 23 24 static struct gpio_desc *halt_gpio; 25 static int halt_irq; 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 pr_info("triggering GPIO.\n"); 44 45 /* Probably wont return */ 46 gpiod_set_value(halt_gpio, 1); 47 48 panic("Halt failed\n"); 49 } 50 51 /* This IRQ means someone pressed the power button and it is waiting for us 52 * to handle the shutdown/poweroff. */ 53 static irqreturn_t gpio_halt_irq(int irq, void *__data) 54 { 55 struct platform_device *pdev = __data; 56 57 dev_info(&pdev->dev, "scheduling shutdown due to power button IRQ\n"); 58 schedule_work(&gpio_halt_wq); 59 60 return IRQ_HANDLED; 61 }; 62 63 static int __gpio_halt_probe(struct platform_device *pdev, 64 struct device_node *halt_node) 65 { 66 int err; 67 68 halt_gpio = fwnode_gpiod_get_index(of_fwnode_handle(halt_node), 69 NULL, 0, GPIOD_OUT_LOW, "gpio-halt"); 70 err = PTR_ERR_OR_ZERO(halt_gpio); 71 if (err) { 72 dev_err(&pdev->dev, "failed to request halt GPIO: %d\n", err); 73 return err; 74 } 75 76 /* Now get the IRQ which tells us when the power button is hit */ 77 halt_irq = irq_of_parse_and_map(halt_node, 0); 78 err = request_irq(halt_irq, gpio_halt_irq, 79 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 80 "gpio-halt", pdev); 81 if (err) { 82 dev_err(&pdev->dev, "failed to request IRQ %d: %d\n", 83 halt_irq, err); 84 gpiod_put(halt_gpio); 85 halt_gpio = NULL; 86 return err; 87 } 88 89 /* Register our halt function */ 90 ppc_md.halt = gpio_halt_cb; 91 pm_power_off = gpio_halt_cb; 92 93 dev_info(&pdev->dev, "registered halt GPIO, irq: %d\n", halt_irq); 94 95 return 0; 96 } 97 98 static int gpio_halt_probe(struct platform_device *pdev) 99 { 100 struct device_node *halt_node; 101 int ret; 102 103 if (!pdev->dev.of_node) 104 return -ENODEV; 105 106 /* If there's no matching child, this isn't really an error */ 107 halt_node = of_find_matching_node(pdev->dev.of_node, child_match); 108 if (!halt_node) 109 return -ENODEV; 110 111 ret = __gpio_halt_probe(pdev, halt_node); 112 of_node_put(halt_node); 113 114 return ret; 115 } 116 117 static int gpio_halt_remove(struct platform_device *pdev) 118 { 119 free_irq(halt_irq, pdev); 120 cancel_work_sync(&gpio_halt_wq); 121 122 ppc_md.halt = NULL; 123 pm_power_off = NULL; 124 125 gpiod_put(halt_gpio); 126 halt_gpio = NULL; 127 128 return 0; 129 } 130 131 static const struct of_device_id gpio_halt_match[] = { 132 /* We match on the gpio bus itself and scan the children since they 133 * wont be matched against us. We know the bus wont match until it 134 * has been registered too. */ 135 { 136 .compatible = "fsl,qoriq-gpio", 137 }, 138 {}, 139 }; 140 MODULE_DEVICE_TABLE(of, gpio_halt_match); 141 142 static struct platform_driver gpio_halt_driver = { 143 .driver = { 144 .name = "gpio-halt", 145 .of_match_table = gpio_halt_match, 146 }, 147 .probe = gpio_halt_probe, 148 .remove = gpio_halt_remove, 149 }; 150 151 module_platform_driver(gpio_halt_driver); 152 153 MODULE_DESCRIPTION("Driver to support GPIO triggered system halt for Servergy CTS-1000 Systems."); 154 MODULE_VERSION("1.0"); 155 MODULE_AUTHOR("Ben Collins <ben.c@servergy.com>"); 156 MODULE_LICENSE("GPL"); 157