12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2e5d76075SStephen Gallimore /* 3e5d76075SStephen Gallimore * Copyright (C) 2013 STMicroelectronics Limited 4e5d76075SStephen Gallimore * Author: Stephen Gallimore <stephen.gallimore@st.com> 5e5d76075SStephen Gallimore * 6e5d76075SStephen Gallimore * Inspired by mach-imx/src.c 7e5d76075SStephen Gallimore */ 8e5d76075SStephen Gallimore #include <linux/kernel.h> 9e5d76075SStephen Gallimore #include <linux/platform_device.h> 10e5d76075SStephen Gallimore #include <linux/module.h> 11e5d76075SStephen Gallimore #include <linux/err.h> 12e5d76075SStephen Gallimore #include <linux/types.h> 13e5d76075SStephen Gallimore #include <linux/of_device.h> 14e5d76075SStephen Gallimore #include <linux/regmap.h> 15e5d76075SStephen Gallimore #include <linux/mfd/syscon.h> 16e5d76075SStephen Gallimore 17e5d76075SStephen Gallimore #include "reset-syscfg.h" 18e5d76075SStephen Gallimore 19e5d76075SStephen Gallimore /** 2068a21516SAlain Volmat * struct syscfg_reset_channel - Reset channel regmap configuration 21e5d76075SStephen Gallimore * 22e5d76075SStephen Gallimore * @reset: regmap field for the channel's reset bit. 23e5d76075SStephen Gallimore * @ack: regmap field for the channel's ack bit (optional). 24e5d76075SStephen Gallimore */ 25e5d76075SStephen Gallimore struct syscfg_reset_channel { 26e5d76075SStephen Gallimore struct regmap_field *reset; 27e5d76075SStephen Gallimore struct regmap_field *ack; 28e5d76075SStephen Gallimore }; 29e5d76075SStephen Gallimore 30e5d76075SStephen Gallimore /** 3168a21516SAlain Volmat * struct syscfg_reset_controller - A reset controller which groups together 3268a21516SAlain Volmat * a set of related reset bits, which may be located in different system 3368a21516SAlain Volmat * configuration registers. 34e5d76075SStephen Gallimore * 35e5d76075SStephen Gallimore * @rst: base reset controller structure. 36e5d76075SStephen Gallimore * @active_low: are the resets in this controller active low, i.e. clearing 37e5d76075SStephen Gallimore * the reset bit puts the hardware into reset. 38e5d76075SStephen Gallimore * @channels: An array of reset channels for this controller. 39e5d76075SStephen Gallimore */ 40e5d76075SStephen Gallimore struct syscfg_reset_controller { 41e5d76075SStephen Gallimore struct reset_controller_dev rst; 42e5d76075SStephen Gallimore bool active_low; 43e5d76075SStephen Gallimore struct syscfg_reset_channel *channels; 44e5d76075SStephen Gallimore }; 45e5d76075SStephen Gallimore 46e5d76075SStephen Gallimore #define to_syscfg_reset_controller(_rst) \ 47e5d76075SStephen Gallimore container_of(_rst, struct syscfg_reset_controller, rst) 48e5d76075SStephen Gallimore 49e5d76075SStephen Gallimore static int syscfg_reset_program_hw(struct reset_controller_dev *rcdev, 50e5d76075SStephen Gallimore unsigned long idx, int assert) 51e5d76075SStephen Gallimore { 52e5d76075SStephen Gallimore struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev); 53e5d76075SStephen Gallimore const struct syscfg_reset_channel *ch; 54e5d76075SStephen Gallimore u32 ctrl_val = rst->active_low ? !assert : !!assert; 55e5d76075SStephen Gallimore int err; 56e5d76075SStephen Gallimore 57e5d76075SStephen Gallimore if (idx >= rcdev->nr_resets) 58e5d76075SStephen Gallimore return -EINVAL; 59e5d76075SStephen Gallimore 60e5d76075SStephen Gallimore ch = &rst->channels[idx]; 61e5d76075SStephen Gallimore 62e5d76075SStephen Gallimore err = regmap_field_write(ch->reset, ctrl_val); 63e5d76075SStephen Gallimore if (err) 64e5d76075SStephen Gallimore return err; 65e5d76075SStephen Gallimore 66e5d76075SStephen Gallimore if (ch->ack) { 67e5d76075SStephen Gallimore unsigned long timeout = jiffies + msecs_to_jiffies(1000); 68e5d76075SStephen Gallimore u32 ack_val; 69e5d76075SStephen Gallimore 70e5d76075SStephen Gallimore while (true) { 71e5d76075SStephen Gallimore err = regmap_field_read(ch->ack, &ack_val); 72e5d76075SStephen Gallimore if (err) 73e5d76075SStephen Gallimore return err; 74e5d76075SStephen Gallimore 75e5d76075SStephen Gallimore if (ack_val == ctrl_val) 76e5d76075SStephen Gallimore break; 77e5d76075SStephen Gallimore 78e5d76075SStephen Gallimore if (time_after(jiffies, timeout)) 79e5d76075SStephen Gallimore return -ETIME; 80e5d76075SStephen Gallimore 81e5d76075SStephen Gallimore cpu_relax(); 82e5d76075SStephen Gallimore } 83e5d76075SStephen Gallimore } 84e5d76075SStephen Gallimore 85e5d76075SStephen Gallimore return 0; 86e5d76075SStephen Gallimore } 87e5d76075SStephen Gallimore 88e5d76075SStephen Gallimore static int syscfg_reset_assert(struct reset_controller_dev *rcdev, 89e5d76075SStephen Gallimore unsigned long idx) 90e5d76075SStephen Gallimore { 91e5d76075SStephen Gallimore return syscfg_reset_program_hw(rcdev, idx, true); 92e5d76075SStephen Gallimore } 93e5d76075SStephen Gallimore 94e5d76075SStephen Gallimore static int syscfg_reset_deassert(struct reset_controller_dev *rcdev, 95e5d76075SStephen Gallimore unsigned long idx) 96e5d76075SStephen Gallimore { 97e5d76075SStephen Gallimore return syscfg_reset_program_hw(rcdev, idx, false); 98e5d76075SStephen Gallimore } 99e5d76075SStephen Gallimore 100e5d76075SStephen Gallimore static int syscfg_reset_dev(struct reset_controller_dev *rcdev, 101e5d76075SStephen Gallimore unsigned long idx) 102e5d76075SStephen Gallimore { 10304378384SPhilipp Zabel int err; 10404378384SPhilipp Zabel 10504378384SPhilipp Zabel err = syscfg_reset_assert(rcdev, idx); 106e5d76075SStephen Gallimore if (err) 107e5d76075SStephen Gallimore return err; 108e5d76075SStephen Gallimore 109e5d76075SStephen Gallimore return syscfg_reset_deassert(rcdev, idx); 110e5d76075SStephen Gallimore } 111e5d76075SStephen Gallimore 1129a4cc897SLee Jones static int syscfg_reset_status(struct reset_controller_dev *rcdev, 1139a4cc897SLee Jones unsigned long idx) 1149a4cc897SLee Jones { 1159a4cc897SLee Jones struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev); 1169a4cc897SLee Jones const struct syscfg_reset_channel *ch; 1179a4cc897SLee Jones u32 ret_val = 0; 1189a4cc897SLee Jones int err; 1199a4cc897SLee Jones 1209a4cc897SLee Jones if (idx >= rcdev->nr_resets) 1219a4cc897SLee Jones return -EINVAL; 1229a4cc897SLee Jones 1239a4cc897SLee Jones ch = &rst->channels[idx]; 1249a4cc897SLee Jones if (ch->ack) 1259a4cc897SLee Jones err = regmap_field_read(ch->ack, &ret_val); 1269a4cc897SLee Jones else 1279a4cc897SLee Jones err = regmap_field_read(ch->reset, &ret_val); 1289a4cc897SLee Jones if (err) 1299a4cc897SLee Jones return err; 1309a4cc897SLee Jones 1319a4cc897SLee Jones return rst->active_low ? !ret_val : !!ret_val; 1329a4cc897SLee Jones } 1339a4cc897SLee Jones 134f673ed4dSPhilipp Zabel static const struct reset_control_ops syscfg_reset_ops = { 135e5d76075SStephen Gallimore .reset = syscfg_reset_dev, 136e5d76075SStephen Gallimore .assert = syscfg_reset_assert, 137e5d76075SStephen Gallimore .deassert = syscfg_reset_deassert, 1389a4cc897SLee Jones .status = syscfg_reset_status, 139e5d76075SStephen Gallimore }; 140e5d76075SStephen Gallimore 141e5d76075SStephen Gallimore static int syscfg_reset_controller_register(struct device *dev, 142e5d76075SStephen Gallimore const struct syscfg_reset_controller_data *data) 143e5d76075SStephen Gallimore { 144e5d76075SStephen Gallimore struct syscfg_reset_controller *rc; 145e5d76075SStephen Gallimore int i, err; 146e5d76075SStephen Gallimore 147e5d76075SStephen Gallimore rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL); 148e5d76075SStephen Gallimore if (!rc) 149e5d76075SStephen Gallimore return -ENOMEM; 150e5d76075SStephen Gallimore 151aa248927SMarkus Elfring rc->channels = devm_kcalloc(dev, data->nr_channels, 152aa248927SMarkus Elfring sizeof(*rc->channels), GFP_KERNEL); 153e5d76075SStephen Gallimore if (!rc->channels) 154e5d76075SStephen Gallimore return -ENOMEM; 155e5d76075SStephen Gallimore 156*71400c3fSPhilipp Zabel rc->rst.ops = &syscfg_reset_ops; 157e5d76075SStephen Gallimore rc->rst.of_node = dev->of_node; 158e5d76075SStephen Gallimore rc->rst.nr_resets = data->nr_channels; 159e5d76075SStephen Gallimore rc->active_low = data->active_low; 160e5d76075SStephen Gallimore 161e5d76075SStephen Gallimore for (i = 0; i < data->nr_channels; i++) { 162e5d76075SStephen Gallimore struct regmap *map; 163e5d76075SStephen Gallimore struct regmap_field *f; 164e5d76075SStephen Gallimore const char *compatible = data->channels[i].compatible; 165e5d76075SStephen Gallimore 166e5d76075SStephen Gallimore map = syscon_regmap_lookup_by_compatible(compatible); 167e5d76075SStephen Gallimore if (IS_ERR(map)) 168e5d76075SStephen Gallimore return PTR_ERR(map); 169e5d76075SStephen Gallimore 170e5d76075SStephen Gallimore f = devm_regmap_field_alloc(dev, map, data->channels[i].reset); 171e5d76075SStephen Gallimore if (IS_ERR(f)) 172e5d76075SStephen Gallimore return PTR_ERR(f); 173e5d76075SStephen Gallimore 174e5d76075SStephen Gallimore rc->channels[i].reset = f; 175e5d76075SStephen Gallimore 176e5d76075SStephen Gallimore if (!data->wait_for_ack) 177e5d76075SStephen Gallimore continue; 178e5d76075SStephen Gallimore 179e5d76075SStephen Gallimore f = devm_regmap_field_alloc(dev, map, data->channels[i].ack); 180e5d76075SStephen Gallimore if (IS_ERR(f)) 181e5d76075SStephen Gallimore return PTR_ERR(f); 182e5d76075SStephen Gallimore 183e5d76075SStephen Gallimore rc->channels[i].ack = f; 184e5d76075SStephen Gallimore } 185e5d76075SStephen Gallimore 186e5d76075SStephen Gallimore err = reset_controller_register(&rc->rst); 187e5d76075SStephen Gallimore if (!err) 188e5d76075SStephen Gallimore dev_info(dev, "registered\n"); 189e5d76075SStephen Gallimore 190e5d76075SStephen Gallimore return err; 191e5d76075SStephen Gallimore } 192e5d76075SStephen Gallimore 193e5d76075SStephen Gallimore int syscfg_reset_probe(struct platform_device *pdev) 194e5d76075SStephen Gallimore { 195e5d76075SStephen Gallimore struct device *dev = pdev ? &pdev->dev : NULL; 196e5d76075SStephen Gallimore const struct of_device_id *match; 197e5d76075SStephen Gallimore 198e5d76075SStephen Gallimore if (!dev || !dev->driver) 199e5d76075SStephen Gallimore return -ENODEV; 200e5d76075SStephen Gallimore 201e5d76075SStephen Gallimore match = of_match_device(dev->driver->of_match_table, dev); 202e5d76075SStephen Gallimore if (!match || !match->data) 203e5d76075SStephen Gallimore return -EINVAL; 204e5d76075SStephen Gallimore 205e5d76075SStephen Gallimore return syscfg_reset_controller_register(dev, match->data); 206e5d76075SStephen Gallimore } 207