1 /* 2 * Toggles a GPIO pin to restart a device 3 * 4 * Copyright (C) 2014 Google, Inc. 5 * 6 * This software is licensed under the terms of the GNU General Public 7 * License version 2, as published by the Free Software Foundation, and 8 * may be copied, distributed, and modified under those terms. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * Based on the gpio-poweroff driver. 16 */ 17 #include <linux/reboot.h> 18 #include <linux/kernel.h> 19 #include <linux/init.h> 20 #include <linux/delay.h> 21 #include <linux/platform_device.h> 22 #include <linux/gpio/consumer.h> 23 #include <linux/of_platform.h> 24 #include <linux/module.h> 25 26 struct gpio_restart { 27 struct gpio_desc *reset_gpio; 28 struct notifier_block restart_handler; 29 u32 active_delay_ms; 30 u32 inactive_delay_ms; 31 u32 wait_delay_ms; 32 }; 33 34 static int gpio_restart_notify(struct notifier_block *this, 35 unsigned long mode, void *cmd) 36 { 37 struct gpio_restart *gpio_restart = 38 container_of(this, struct gpio_restart, restart_handler); 39 40 /* drive it active, also inactive->active edge */ 41 gpiod_direction_output(gpio_restart->reset_gpio, 1); 42 mdelay(gpio_restart->active_delay_ms); 43 44 /* drive inactive, also active->inactive edge */ 45 gpiod_set_value(gpio_restart->reset_gpio, 0); 46 mdelay(gpio_restart->inactive_delay_ms); 47 48 /* drive it active, also inactive->active edge */ 49 gpiod_set_value(gpio_restart->reset_gpio, 1); 50 51 /* give it some time */ 52 mdelay(gpio_restart->wait_delay_ms); 53 54 WARN_ON(1); 55 56 return NOTIFY_DONE; 57 } 58 59 static int gpio_restart_probe(struct platform_device *pdev) 60 { 61 struct gpio_restart *gpio_restart; 62 bool open_source = false; 63 u32 property; 64 int ret; 65 66 gpio_restart = devm_kzalloc(&pdev->dev, sizeof(*gpio_restart), 67 GFP_KERNEL); 68 if (!gpio_restart) 69 return -ENOMEM; 70 71 open_source = of_property_read_bool(pdev->dev.of_node, "open-source"); 72 73 gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL, 74 open_source ? GPIOD_IN : GPIOD_OUT_LOW); 75 if (IS_ERR(gpio_restart->reset_gpio)) { 76 dev_err(&pdev->dev, "Could net get reset GPIO\n"); 77 return PTR_ERR(gpio_restart->reset_gpio); 78 } 79 80 gpio_restart->restart_handler.notifier_call = gpio_restart_notify; 81 gpio_restart->restart_handler.priority = 128; 82 gpio_restart->active_delay_ms = 100; 83 gpio_restart->inactive_delay_ms = 100; 84 gpio_restart->wait_delay_ms = 3000; 85 86 ret = of_property_read_u32(pdev->dev.of_node, "priority", &property); 87 if (!ret) { 88 if (property > 255) 89 dev_err(&pdev->dev, "Invalid priority property: %u\n", 90 property); 91 else 92 gpio_restart->restart_handler.priority = property; 93 } 94 95 of_property_read_u32(pdev->dev.of_node, "active-delay", 96 &gpio_restart->active_delay_ms); 97 of_property_read_u32(pdev->dev.of_node, "inactive-delay", 98 &gpio_restart->inactive_delay_ms); 99 of_property_read_u32(pdev->dev.of_node, "wait-delay", 100 &gpio_restart->wait_delay_ms); 101 102 platform_set_drvdata(pdev, gpio_restart); 103 104 ret = register_restart_handler(&gpio_restart->restart_handler); 105 if (ret) { 106 dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n", 107 __func__, ret); 108 return -ENODEV; 109 } 110 111 return 0; 112 } 113 114 static int gpio_restart_remove(struct platform_device *pdev) 115 { 116 struct gpio_restart *gpio_restart = platform_get_drvdata(pdev); 117 int ret; 118 119 ret = unregister_restart_handler(&gpio_restart->restart_handler); 120 if (ret) { 121 dev_err(&pdev->dev, 122 "%s: cannot unregister restart handler, %d\n", 123 __func__, ret); 124 return -ENODEV; 125 } 126 127 return 0; 128 } 129 130 static const struct of_device_id of_gpio_restart_match[] = { 131 { .compatible = "gpio-restart", }, 132 {}, 133 }; 134 135 static struct platform_driver gpio_restart_driver = { 136 .probe = gpio_restart_probe, 137 .remove = gpio_restart_remove, 138 .driver = { 139 .name = "restart-gpio", 140 .of_match_table = of_gpio_restart_match, 141 }, 142 }; 143 144 module_platform_driver(gpio_restart_driver); 145 146 MODULE_AUTHOR("David Riley <davidriley@chromium.org>"); 147 MODULE_DESCRIPTION("GPIO restart driver"); 148 MODULE_LICENSE("GPL"); 149