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 24static struct gpio_desc *halt_gpio; 25static int halt_irq; 26 27static const struct of_device_id child_match[] = { 28 { 29 .compatible = "sgy,gpio-halt", 30 }, 31 {}, 32}; 33 34static void gpio_halt_wfn(struct work_struct *work) 35{ 36 /* Likely wont return */ 37 orderly_poweroff(true); 38} 39static DECLARE_WORK(gpio_halt_wq, gpio_halt_wfn); 40 41static 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. */ 53static 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 63static 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 98static 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 117static 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 131static 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}; 140MODULE_DEVICE_TABLE(of, gpio_halt_match); 141 142static 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 151module_platform_driver(gpio_halt_driver); 152 153MODULE_DESCRIPTION("Driver to support GPIO triggered system halt for Servergy CTS-1000 Systems."); 154MODULE_VERSION("1.0"); 155MODULE_AUTHOR("Ben Collins <ben.c@servergy.com>"); 156MODULE_LICENSE("GPL"); 157