116 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Toggles a GPIO pin to power down a device4 *5 * Jamie Lentin <jm@lentin.co.uk>6 * Andrew Lunn <andrew@lunn.ch>7 *8 * Copyright (C) 2012 Jamie Lentin9 */10#include <linux/kernel.h>11#include <linux/init.h>12#include <linux/delay.h>13#include <linux/platform_device.h>14#include <linux/property.h>15#include <linux/gpio/consumer.h>16#include <linux/mod_devicetable.h>17#include <linux/module.h>18#include <linux/reboot.h>19 20#define DEFAULT_TIMEOUT_MS 300021 22struct gpio_poweroff {23 struct gpio_desc *reset_gpio;24 u32 timeout_ms;25 u32 active_delay_ms;26 u32 inactive_delay_ms;27};28 29static int gpio_poweroff_do_poweroff(struct sys_off_data *data)30{31 struct gpio_poweroff *gpio_poweroff = data->cb_data;32 33 /* drive it active, also inactive->active edge */34 gpiod_direction_output(gpio_poweroff->reset_gpio, 1);35 mdelay(gpio_poweroff->active_delay_ms);36 37 /* drive inactive, also active->inactive edge */38 gpiod_set_value_cansleep(gpio_poweroff->reset_gpio, 0);39 mdelay(gpio_poweroff->inactive_delay_ms);40 41 /* drive it active, also inactive->active edge */42 gpiod_set_value_cansleep(gpio_poweroff->reset_gpio, 1);43 44 /* give it some time */45 mdelay(gpio_poweroff->timeout_ms);46 47 WARN_ON(1);48 49 return NOTIFY_DONE;50}51 52static int gpio_poweroff_probe(struct platform_device *pdev)53{54 struct gpio_poweroff *gpio_poweroff;55 bool input = false;56 enum gpiod_flags flags;57 int priority = SYS_OFF_PRIO_DEFAULT;58 int ret;59 60 gpio_poweroff = devm_kzalloc(&pdev->dev, sizeof(*gpio_poweroff), GFP_KERNEL);61 if (!gpio_poweroff)62 return -ENOMEM;63 64 input = device_property_read_bool(&pdev->dev, "input");65 if (input)66 flags = GPIOD_IN;67 else68 flags = GPIOD_OUT_LOW;69 70 71 gpio_poweroff->active_delay_ms = 100;72 gpio_poweroff->inactive_delay_ms = 100;73 gpio_poweroff->timeout_ms = DEFAULT_TIMEOUT_MS;74 75 device_property_read_u32(&pdev->dev, "active-delay-ms", &gpio_poweroff->active_delay_ms);76 device_property_read_u32(&pdev->dev, "inactive-delay-ms",77 &gpio_poweroff->inactive_delay_ms);78 device_property_read_u32(&pdev->dev, "timeout-ms", &gpio_poweroff->timeout_ms);79 device_property_read_u32(&pdev->dev, "priority", &priority);80 if (priority > 255) {81 dev_err(&pdev->dev, "Invalid priority property: %u\n", priority);82 return -EINVAL;83 }84 85 gpio_poweroff->reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);86 if (IS_ERR(gpio_poweroff->reset_gpio))87 return PTR_ERR(gpio_poweroff->reset_gpio);88 89 ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF,90 priority, gpio_poweroff_do_poweroff, gpio_poweroff);91 if (ret)92 return dev_err_probe(&pdev->dev, ret, "Cannot register poweroff handler\n");93 94 return 0;95}96 97static const struct of_device_id of_gpio_poweroff_match[] = {98 { .compatible = "gpio-poweroff", },99 {},100};101MODULE_DEVICE_TABLE(of, of_gpio_poweroff_match);102 103static struct platform_driver gpio_poweroff_driver = {104 .probe = gpio_poweroff_probe,105 .driver = {106 .name = "poweroff-gpio",107 .of_match_table = of_gpio_poweroff_match,108 },109};110 111module_platform_driver(gpio_poweroff_driver);112 113MODULE_AUTHOR("Jamie Lentin <jm@lentin.co.uk>");114MODULE_DESCRIPTION("GPIO poweroff driver");115MODULE_ALIAS("platform:poweroff-gpio");116