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 /** 20e5d76075SStephen Gallimore * 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 /** 31e5d76075SStephen Gallimore * A reset controller which groups together a set of related reset bits, which 32e5d76075SStephen Gallimore * may be located in different system configuration registers. 33e5d76075SStephen Gallimore * 34e5d76075SStephen Gallimore * @rst: base reset controller structure. 35e5d76075SStephen Gallimore * @active_low: are the resets in this controller active low, i.e. clearing 36e5d76075SStephen Gallimore * the reset bit puts the hardware into reset. 37e5d76075SStephen Gallimore * @channels: An array of reset channels for this controller. 38e5d76075SStephen Gallimore */ 39e5d76075SStephen Gallimore struct syscfg_reset_controller { 40e5d76075SStephen Gallimore struct reset_controller_dev rst; 41e5d76075SStephen Gallimore bool active_low; 42e5d76075SStephen Gallimore struct syscfg_reset_channel *channels; 43e5d76075SStephen Gallimore }; 44e5d76075SStephen Gallimore 45e5d76075SStephen Gallimore #define to_syscfg_reset_controller(_rst) \ 46e5d76075SStephen Gallimore container_of(_rst, struct syscfg_reset_controller, rst) 47e5d76075SStephen Gallimore 48e5d76075SStephen Gallimore static int syscfg_reset_program_hw(struct reset_controller_dev *rcdev, 49e5d76075SStephen Gallimore unsigned long idx, int assert) 50e5d76075SStephen Gallimore { 51e5d76075SStephen Gallimore struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev); 52e5d76075SStephen Gallimore const struct syscfg_reset_channel *ch; 53e5d76075SStephen Gallimore u32 ctrl_val = rst->active_low ? !assert : !!assert; 54e5d76075SStephen Gallimore int err; 55e5d76075SStephen Gallimore 56e5d76075SStephen Gallimore if (idx >= rcdev->nr_resets) 57e5d76075SStephen Gallimore return -EINVAL; 58e5d76075SStephen Gallimore 59e5d76075SStephen Gallimore ch = &rst->channels[idx]; 60e5d76075SStephen Gallimore 61e5d76075SStephen Gallimore err = regmap_field_write(ch->reset, ctrl_val); 62e5d76075SStephen Gallimore if (err) 63e5d76075SStephen Gallimore return err; 64e5d76075SStephen Gallimore 65e5d76075SStephen Gallimore if (ch->ack) { 66e5d76075SStephen Gallimore unsigned long timeout = jiffies + msecs_to_jiffies(1000); 67e5d76075SStephen Gallimore u32 ack_val; 68e5d76075SStephen Gallimore 69e5d76075SStephen Gallimore while (true) { 70e5d76075SStephen Gallimore err = regmap_field_read(ch->ack, &ack_val); 71e5d76075SStephen Gallimore if (err) 72e5d76075SStephen Gallimore return err; 73e5d76075SStephen Gallimore 74e5d76075SStephen Gallimore if (ack_val == ctrl_val) 75e5d76075SStephen Gallimore break; 76e5d76075SStephen Gallimore 77e5d76075SStephen Gallimore if (time_after(jiffies, timeout)) 78e5d76075SStephen Gallimore return -ETIME; 79e5d76075SStephen Gallimore 80e5d76075SStephen Gallimore cpu_relax(); 81e5d76075SStephen Gallimore } 82e5d76075SStephen Gallimore } 83e5d76075SStephen Gallimore 84e5d76075SStephen Gallimore return 0; 85e5d76075SStephen Gallimore } 86e5d76075SStephen Gallimore 87e5d76075SStephen Gallimore static int syscfg_reset_assert(struct reset_controller_dev *rcdev, 88e5d76075SStephen Gallimore unsigned long idx) 89e5d76075SStephen Gallimore { 90e5d76075SStephen Gallimore return syscfg_reset_program_hw(rcdev, idx, true); 91e5d76075SStephen Gallimore } 92e5d76075SStephen Gallimore 93e5d76075SStephen Gallimore static int syscfg_reset_deassert(struct reset_controller_dev *rcdev, 94e5d76075SStephen Gallimore unsigned long idx) 95e5d76075SStephen Gallimore { 96e5d76075SStephen Gallimore return syscfg_reset_program_hw(rcdev, idx, false); 97e5d76075SStephen Gallimore } 98e5d76075SStephen Gallimore 99e5d76075SStephen Gallimore static int syscfg_reset_dev(struct reset_controller_dev *rcdev, 100e5d76075SStephen Gallimore unsigned long idx) 101e5d76075SStephen Gallimore { 10204378384SPhilipp Zabel int err; 10304378384SPhilipp Zabel 10404378384SPhilipp Zabel err = syscfg_reset_assert(rcdev, idx); 105e5d76075SStephen Gallimore if (err) 106e5d76075SStephen Gallimore return err; 107e5d76075SStephen Gallimore 108e5d76075SStephen Gallimore return syscfg_reset_deassert(rcdev, idx); 109e5d76075SStephen Gallimore } 110e5d76075SStephen Gallimore 1119a4cc897SLee Jones static int syscfg_reset_status(struct reset_controller_dev *rcdev, 1129a4cc897SLee Jones unsigned long idx) 1139a4cc897SLee Jones { 1149a4cc897SLee Jones struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev); 1159a4cc897SLee Jones const struct syscfg_reset_channel *ch; 1169a4cc897SLee Jones u32 ret_val = 0; 1179a4cc897SLee Jones int err; 1189a4cc897SLee Jones 1199a4cc897SLee Jones if (idx >= rcdev->nr_resets) 1209a4cc897SLee Jones return -EINVAL; 1219a4cc897SLee Jones 1229a4cc897SLee Jones ch = &rst->channels[idx]; 1239a4cc897SLee Jones if (ch->ack) 1249a4cc897SLee Jones err = regmap_field_read(ch->ack, &ret_val); 1259a4cc897SLee Jones else 1269a4cc897SLee Jones err = regmap_field_read(ch->reset, &ret_val); 1279a4cc897SLee Jones if (err) 1289a4cc897SLee Jones return err; 1299a4cc897SLee Jones 1309a4cc897SLee Jones return rst->active_low ? !ret_val : !!ret_val; 1319a4cc897SLee Jones } 1329a4cc897SLee Jones 133f673ed4dSPhilipp Zabel static const struct reset_control_ops syscfg_reset_ops = { 134e5d76075SStephen Gallimore .reset = syscfg_reset_dev, 135e5d76075SStephen Gallimore .assert = syscfg_reset_assert, 136e5d76075SStephen Gallimore .deassert = syscfg_reset_deassert, 1379a4cc897SLee Jones .status = syscfg_reset_status, 138e5d76075SStephen Gallimore }; 139e5d76075SStephen Gallimore 140e5d76075SStephen Gallimore static int syscfg_reset_controller_register(struct device *dev, 141e5d76075SStephen Gallimore const struct syscfg_reset_controller_data *data) 142e5d76075SStephen Gallimore { 143e5d76075SStephen Gallimore struct syscfg_reset_controller *rc; 144e5d76075SStephen Gallimore int i, err; 145e5d76075SStephen Gallimore 146e5d76075SStephen Gallimore rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL); 147e5d76075SStephen Gallimore if (!rc) 148e5d76075SStephen Gallimore return -ENOMEM; 149e5d76075SStephen Gallimore 150aa248927SMarkus Elfring rc->channels = devm_kcalloc(dev, data->nr_channels, 151aa248927SMarkus Elfring sizeof(*rc->channels), GFP_KERNEL); 152e5d76075SStephen Gallimore if (!rc->channels) 153e5d76075SStephen Gallimore return -ENOMEM; 154e5d76075SStephen Gallimore 155e5d76075SStephen Gallimore rc->rst.ops = &syscfg_reset_ops, 156e5d76075SStephen Gallimore rc->rst.of_node = dev->of_node; 157e5d76075SStephen Gallimore rc->rst.nr_resets = data->nr_channels; 158e5d76075SStephen Gallimore rc->active_low = data->active_low; 159e5d76075SStephen Gallimore 160e5d76075SStephen Gallimore for (i = 0; i < data->nr_channels; i++) { 161e5d76075SStephen Gallimore struct regmap *map; 162e5d76075SStephen Gallimore struct regmap_field *f; 163e5d76075SStephen Gallimore const char *compatible = data->channels[i].compatible; 164e5d76075SStephen Gallimore 165e5d76075SStephen Gallimore map = syscon_regmap_lookup_by_compatible(compatible); 166e5d76075SStephen Gallimore if (IS_ERR(map)) 167e5d76075SStephen Gallimore return PTR_ERR(map); 168e5d76075SStephen Gallimore 169e5d76075SStephen Gallimore f = devm_regmap_field_alloc(dev, map, data->channels[i].reset); 170e5d76075SStephen Gallimore if (IS_ERR(f)) 171e5d76075SStephen Gallimore return PTR_ERR(f); 172e5d76075SStephen Gallimore 173e5d76075SStephen Gallimore rc->channels[i].reset = f; 174e5d76075SStephen Gallimore 175e5d76075SStephen Gallimore if (!data->wait_for_ack) 176e5d76075SStephen Gallimore continue; 177e5d76075SStephen Gallimore 178e5d76075SStephen Gallimore f = devm_regmap_field_alloc(dev, map, data->channels[i].ack); 179e5d76075SStephen Gallimore if (IS_ERR(f)) 180e5d76075SStephen Gallimore return PTR_ERR(f); 181e5d76075SStephen Gallimore 182e5d76075SStephen Gallimore rc->channels[i].ack = f; 183e5d76075SStephen Gallimore } 184e5d76075SStephen Gallimore 185e5d76075SStephen Gallimore err = reset_controller_register(&rc->rst); 186e5d76075SStephen Gallimore if (!err) 187e5d76075SStephen Gallimore dev_info(dev, "registered\n"); 188e5d76075SStephen Gallimore 189e5d76075SStephen Gallimore return err; 190e5d76075SStephen Gallimore } 191e5d76075SStephen Gallimore 192e5d76075SStephen Gallimore int syscfg_reset_probe(struct platform_device *pdev) 193e5d76075SStephen Gallimore { 194e5d76075SStephen Gallimore struct device *dev = pdev ? &pdev->dev : NULL; 195e5d76075SStephen Gallimore const struct of_device_id *match; 196e5d76075SStephen Gallimore 197e5d76075SStephen Gallimore if (!dev || !dev->driver) 198e5d76075SStephen Gallimore return -ENODEV; 199e5d76075SStephen Gallimore 200e5d76075SStephen Gallimore match = of_match_device(dev->driver->of_match_table, dev); 201e5d76075SStephen Gallimore if (!match || !match->data) 202e5d76075SStephen Gallimore return -EINVAL; 203e5d76075SStephen Gallimore 204e5d76075SStephen Gallimore return syscfg_reset_controller_register(dev, match->data); 205e5d76075SStephen Gallimore } 206