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