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