sgy_cts1000.c (4049B)
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 21static struct device_node *halt_node; 22 23static const struct of_device_id child_match[] = { 24 { 25 .compatible = "sgy,gpio-halt", 26 }, 27 {}, 28}; 29 30static void gpio_halt_wfn(struct work_struct *work) 31{ 32 /* Likely wont return */ 33 orderly_poweroff(true); 34} 35static DECLARE_WORK(gpio_halt_wq, gpio_halt_wfn); 36 37static 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. */ 62static 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 70static 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 129static 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 148static 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}; 157MODULE_DEVICE_TABLE(of, gpio_halt_match); 158 159static 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 168module_platform_driver(gpio_halt_driver); 169 170MODULE_DESCRIPTION("Driver to support GPIO triggered system halt for Servergy CTS-1000 Systems."); 171MODULE_VERSION("1.0"); 172MODULE_AUTHOR("Ben Collins <ben.c@servergy.com>"); 173MODULE_LICENSE("GPL");