linux/arch/powerpc/platforms/85xx/sgy_cts1000.c
<<
>>
Prefs
   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");
 174