linux/arch/powerpc/platforms/85xx/sgy_cts1000.c
<<
>>
Prefs
   1/*
   2 * Servergy CTS-1000 Setup
   3 *
   4 * Maintained by Ben Collins <ben.c@servergy.com>
   5 *
   6 * Copyright 2012 by Servergy, Inc.
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 */
  13
  14#include <linux/platform_device.h>
  15#include <linux/device.h>
  16#include <linux/module.h>
  17#include <linux/init.h>
  18#include <linux/of_gpio.h>
  19#include <linux/workqueue.h>
  20#include <linux/reboot.h>
  21#include <linux/interrupt.h>
  22
  23#include <asm/machdep.h>
  24
  25static struct device_node *halt_node;
  26
  27static 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 gpio_halt_cb(void)
  42{
  43        enum of_gpio_flags flags;
  44        int trigger, gpio;
  45
  46        if (!halt_node)
  47                return;
  48
  49        gpio = of_get_gpio_flags(halt_node, 0, &flags);
  50
  51        if (!gpio_is_valid(gpio))
  52                return;
  53
  54        trigger = (flags == OF_GPIO_ACTIVE_LOW);
  55
  56        printk(KERN_INFO "gpio-halt: triggering GPIO.\n");
  57
  58        /* Probably wont return */
  59        gpio_set_value(gpio, trigger);
  60}
  61
  62/* This IRQ means someone pressed the power button and it is waiting for us
  63 * to handle the shutdown/poweroff. */
  64static irqreturn_t gpio_halt_irq(int irq, void *__data)
  65{
  66        printk(KERN_INFO "gpio-halt: shutdown due to power button IRQ.\n");
  67        schedule_work(&gpio_halt_wq);
  68
  69        return IRQ_HANDLED;
  70};
  71
  72static int gpio_halt_probe(struct platform_device *pdev)
  73{
  74        enum of_gpio_flags flags;
  75        struct device_node *node = pdev->dev.of_node;
  76        int gpio, err, irq;
  77        int trigger;
  78
  79        if (!node)
  80                return -ENODEV;
  81
  82        /* If there's no matching child, this isn't really an error */
  83        halt_node = of_find_matching_node(node, child_match);
  84        if (!halt_node)
  85                return 0;
  86
  87        /* Technically we could just read the first one, but punish
  88         * DT writers for invalid form. */
  89        if (of_gpio_count(halt_node) != 1)
  90                return -EINVAL;
  91
  92        /* Get the gpio number relative to the dynamic base. */
  93        gpio = of_get_gpio_flags(halt_node, 0, &flags);
  94        if (!gpio_is_valid(gpio))
  95                return -EINVAL;
  96
  97        err = gpio_request(gpio, "gpio-halt");
  98        if (err) {
  99                printk(KERN_ERR "gpio-halt: error requesting GPIO %d.\n",
 100                       gpio);
 101                halt_node = NULL;
 102                return err;
 103        }
 104
 105        trigger = (flags == OF_GPIO_ACTIVE_LOW);
 106
 107        gpio_direction_output(gpio, !trigger);
 108
 109        /* Now get the IRQ which tells us when the power button is hit */
 110        irq = irq_of_parse_and_map(halt_node, 0);
 111        err = request_irq(irq, gpio_halt_irq, IRQF_TRIGGER_RISING |
 112                          IRQF_TRIGGER_FALLING, "gpio-halt", halt_node);
 113        if (err) {
 114                printk(KERN_ERR "gpio-halt: error requesting IRQ %d for "
 115                       "GPIO %d.\n", irq, gpio);
 116                gpio_free(gpio);
 117                halt_node = NULL;
 118                return err;
 119        }
 120
 121        /* Register our halt function */
 122        ppc_md.halt = gpio_halt_cb;
 123        ppc_md.power_off = gpio_halt_cb;
 124
 125        printk(KERN_INFO "gpio-halt: registered GPIO %d (%d trigger, %d"
 126               " irq).\n", gpio, trigger, irq);
 127
 128        return 0;
 129}
 130
 131static int gpio_halt_remove(struct platform_device *pdev)
 132{
 133        if (halt_node) {
 134                int gpio = of_get_gpio(halt_node, 0);
 135                int irq = irq_of_parse_and_map(halt_node, 0);
 136
 137                free_irq(irq, halt_node);
 138
 139                ppc_md.halt = NULL;
 140                ppc_md.power_off = NULL;
 141
 142                gpio_free(gpio);
 143
 144                halt_node = NULL;
 145        }
 146
 147        return 0;
 148}
 149
 150static struct of_device_id gpio_halt_match[] = {
 151        /* We match on the gpio bus itself and scan the children since they
 152         * wont be matched against us. We know the bus wont match until it
 153         * has been registered too. */
 154        {
 155                .compatible = "fsl,qoriq-gpio",
 156        },
 157        {},
 158};
 159MODULE_DEVICE_TABLE(of, gpio_halt_match);
 160
 161static struct platform_driver gpio_halt_driver = {
 162        .driver = {
 163                .name           = "gpio-halt",
 164                .owner          = THIS_MODULE,
 165                .of_match_table = gpio_halt_match,
 166        },
 167        .probe          = gpio_halt_probe,
 168        .remove         = gpio_halt_remove,
 169};
 170
 171module_platform_driver(gpio_halt_driver);
 172
 173MODULE_DESCRIPTION("Driver to support GPIO triggered system halt for Servergy CTS-1000 Systems.");
 174MODULE_VERSION("1.0");
 175MODULE_AUTHOR("Ben Collins <ben.c@servergy.com>");
 176MODULE_LICENSE("GPL");
 177