1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * TI TPS380x Supply Voltage Supervisor and Reset Controller Driver 4 * 5 * Copyright (C) 2022 Pengutronix, Marco Felsch <kernel@pengutronix.de> 6 * 7 * Based on Simple Reset Controller Driver 8 * 9 * Copyright (C) 2017 Pengutronix, Philipp Zabel <kernel@pengutronix.de> 10 */ 11 12#include <linux/delay.h> 13#include <linux/gpio/consumer.h> 14#include <linux/module.h> 15#include <linux/of.h> 16#include <linux/platform_device.h> 17#include <linux/property.h> 18#include <linux/reset-controller.h> 19 20struct tps380x_reset { 21 struct reset_controller_dev rcdev; 22 struct gpio_desc *reset_gpio; 23 unsigned int reset_ms; 24}; 25 26struct tps380x_reset_devdata { 27 unsigned int min_reset_ms; 28 unsigned int typ_reset_ms; 29 unsigned int max_reset_ms; 30}; 31 32static inline 33struct tps380x_reset *to_tps380x_reset(struct reset_controller_dev *rcdev) 34{ 35 return container_of(rcdev, struct tps380x_reset, rcdev); 36} 37 38static int 39tps380x_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) 40{ 41 struct tps380x_reset *tps380x = to_tps380x_reset(rcdev); 42 43 gpiod_set_value_cansleep(tps380x->reset_gpio, 1); 44 45 return 0; 46} 47 48static int 49tps380x_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) 50{ 51 struct tps380x_reset *tps380x = to_tps380x_reset(rcdev); 52 53 gpiod_set_value_cansleep(tps380x->reset_gpio, 0); 54 msleep(tps380x->reset_ms); 55 56 return 0; 57} 58 59static const struct reset_control_ops reset_tps380x_ops = { 60 .assert = tps380x_reset_assert, 61 .deassert = tps380x_reset_deassert, 62}; 63 64static int tps380x_reset_of_xlate(struct reset_controller_dev *rcdev, 65 const struct of_phandle_args *reset_spec) 66{ 67 /* No special handling needed, we have only one reset line per device */ 68 return 0; 69} 70 71static int tps380x_reset_probe(struct platform_device *pdev) 72{ 73 struct device *dev = &pdev->dev; 74 const struct tps380x_reset_devdata *devdata; 75 struct tps380x_reset *tps380x; 76 77 devdata = device_get_match_data(dev); 78 if (!devdata) 79 return -EINVAL; 80 81 tps380x = devm_kzalloc(dev, sizeof(*tps380x), GFP_KERNEL); 82 if (!tps380x) 83 return -ENOMEM; 84 85 tps380x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 86 if (IS_ERR(tps380x->reset_gpio)) 87 return dev_err_probe(dev, PTR_ERR(tps380x->reset_gpio), 88 "Failed to get GPIO\n"); 89 90 tps380x->reset_ms = devdata->max_reset_ms; 91 92 tps380x->rcdev.ops = &reset_tps380x_ops; 93 tps380x->rcdev.owner = THIS_MODULE; 94 tps380x->rcdev.dev = dev; 95 tps380x->rcdev.of_node = dev->of_node; 96 tps380x->rcdev.of_reset_n_cells = 0; 97 tps380x->rcdev.of_xlate = tps380x_reset_of_xlate; 98 tps380x->rcdev.nr_resets = 1; 99 100 return devm_reset_controller_register(dev, &tps380x->rcdev); 101} 102 103static const struct tps380x_reset_devdata tps3801_reset_data = { 104 .min_reset_ms = 120, 105 .typ_reset_ms = 200, 106 .max_reset_ms = 280, 107}; 108 109static const struct of_device_id tps380x_reset_dt_ids[] = { 110 { .compatible = "ti,tps3801", .data = &tps3801_reset_data }, 111 { /* sentinel */ }, 112}; 113MODULE_DEVICE_TABLE(of, tps380x_reset_dt_ids); 114 115static struct platform_driver tps380x_reset_driver = { 116 .probe = tps380x_reset_probe, 117 .driver = { 118 .name = "tps380x-reset", 119 .of_match_table = tps380x_reset_dt_ids, 120 }, 121}; 122module_platform_driver(tps380x_reset_driver); 123 124MODULE_AUTHOR("Marco Felsch <kernel@pengutronix.de>"); 125MODULE_DESCRIPTION("TI TPS380x Supply Voltage Supervisor and Reset Driver"); 126MODULE_LICENSE("GPL v2"); 127