18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013 STMicroelectronics Limited 48c2ecf20Sopenharmony_ci * Author: Stephen Gallimore <stephen.gallimore@st.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Inspired by mach-imx/src.c 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/kernel.h> 98c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/types.h> 138c2ecf20Sopenharmony_ci#include <linux/of_device.h> 148c2ecf20Sopenharmony_ci#include <linux/regmap.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "reset-syscfg.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/** 208c2ecf20Sopenharmony_ci * struct syscfg_reset_channel - Reset channel regmap configuration 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * @reset: regmap field for the channel's reset bit. 238c2ecf20Sopenharmony_ci * @ack: regmap field for the channel's ack bit (optional). 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_cistruct syscfg_reset_channel { 268c2ecf20Sopenharmony_ci struct regmap_field *reset; 278c2ecf20Sopenharmony_ci struct regmap_field *ack; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/** 318c2ecf20Sopenharmony_ci * struct syscfg_reset_controller - A reset controller which groups together 328c2ecf20Sopenharmony_ci * a set of related reset bits, which may be located in different system 338c2ecf20Sopenharmony_ci * configuration registers. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * @rst: base reset controller structure. 368c2ecf20Sopenharmony_ci * @active_low: are the resets in this controller active low, i.e. clearing 378c2ecf20Sopenharmony_ci * the reset bit puts the hardware into reset. 388c2ecf20Sopenharmony_ci * @channels: An array of reset channels for this controller. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistruct syscfg_reset_controller { 418c2ecf20Sopenharmony_ci struct reset_controller_dev rst; 428c2ecf20Sopenharmony_ci bool active_low; 438c2ecf20Sopenharmony_ci struct syscfg_reset_channel *channels; 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define to_syscfg_reset_controller(_rst) \ 478c2ecf20Sopenharmony_ci container_of(_rst, struct syscfg_reset_controller, rst) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int syscfg_reset_program_hw(struct reset_controller_dev *rcdev, 508c2ecf20Sopenharmony_ci unsigned long idx, int assert) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev); 538c2ecf20Sopenharmony_ci const struct syscfg_reset_channel *ch; 548c2ecf20Sopenharmony_ci u32 ctrl_val = rst->active_low ? !assert : !!assert; 558c2ecf20Sopenharmony_ci int err; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (idx >= rcdev->nr_resets) 588c2ecf20Sopenharmony_ci return -EINVAL; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci ch = &rst->channels[idx]; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci err = regmap_field_write(ch->reset, ctrl_val); 638c2ecf20Sopenharmony_ci if (err) 648c2ecf20Sopenharmony_ci return err; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (ch->ack) { 678c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(1000); 688c2ecf20Sopenharmony_ci u32 ack_val; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci while (true) { 718c2ecf20Sopenharmony_ci err = regmap_field_read(ch->ack, &ack_val); 728c2ecf20Sopenharmony_ci if (err) 738c2ecf20Sopenharmony_ci return err; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (ack_val == ctrl_val) 768c2ecf20Sopenharmony_ci break; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 798c2ecf20Sopenharmony_ci return -ETIME; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci cpu_relax(); 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int syscfg_reset_assert(struct reset_controller_dev *rcdev, 898c2ecf20Sopenharmony_ci unsigned long idx) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci return syscfg_reset_program_hw(rcdev, idx, true); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int syscfg_reset_deassert(struct reset_controller_dev *rcdev, 958c2ecf20Sopenharmony_ci unsigned long idx) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci return syscfg_reset_program_hw(rcdev, idx, false); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int syscfg_reset_dev(struct reset_controller_dev *rcdev, 1018c2ecf20Sopenharmony_ci unsigned long idx) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci int err; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci err = syscfg_reset_assert(rcdev, idx); 1068c2ecf20Sopenharmony_ci if (err) 1078c2ecf20Sopenharmony_ci return err; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return syscfg_reset_deassert(rcdev, idx); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int syscfg_reset_status(struct reset_controller_dev *rcdev, 1138c2ecf20Sopenharmony_ci unsigned long idx) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev); 1168c2ecf20Sopenharmony_ci const struct syscfg_reset_channel *ch; 1178c2ecf20Sopenharmony_ci u32 ret_val = 0; 1188c2ecf20Sopenharmony_ci int err; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (idx >= rcdev->nr_resets) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ch = &rst->channels[idx]; 1248c2ecf20Sopenharmony_ci if (ch->ack) 1258c2ecf20Sopenharmony_ci err = regmap_field_read(ch->ack, &ret_val); 1268c2ecf20Sopenharmony_ci else 1278c2ecf20Sopenharmony_ci err = regmap_field_read(ch->reset, &ret_val); 1288c2ecf20Sopenharmony_ci if (err) 1298c2ecf20Sopenharmony_ci return err; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return rst->active_low ? !ret_val : !!ret_val; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic const struct reset_control_ops syscfg_reset_ops = { 1358c2ecf20Sopenharmony_ci .reset = syscfg_reset_dev, 1368c2ecf20Sopenharmony_ci .assert = syscfg_reset_assert, 1378c2ecf20Sopenharmony_ci .deassert = syscfg_reset_deassert, 1388c2ecf20Sopenharmony_ci .status = syscfg_reset_status, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int syscfg_reset_controller_register(struct device *dev, 1428c2ecf20Sopenharmony_ci const struct syscfg_reset_controller_data *data) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct syscfg_reset_controller *rc; 1458c2ecf20Sopenharmony_ci int i, err; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL); 1488c2ecf20Sopenharmony_ci if (!rc) 1498c2ecf20Sopenharmony_ci return -ENOMEM; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci rc->channels = devm_kcalloc(dev, data->nr_channels, 1528c2ecf20Sopenharmony_ci sizeof(*rc->channels), GFP_KERNEL); 1538c2ecf20Sopenharmony_ci if (!rc->channels) 1548c2ecf20Sopenharmony_ci return -ENOMEM; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci rc->rst.ops = &syscfg_reset_ops, 1578c2ecf20Sopenharmony_ci rc->rst.of_node = dev->of_node; 1588c2ecf20Sopenharmony_ci rc->rst.nr_resets = data->nr_channels; 1598c2ecf20Sopenharmony_ci rc->active_low = data->active_low; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci for (i = 0; i < data->nr_channels; i++) { 1628c2ecf20Sopenharmony_ci struct regmap *map; 1638c2ecf20Sopenharmony_ci struct regmap_field *f; 1648c2ecf20Sopenharmony_ci const char *compatible = data->channels[i].compatible; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci map = syscon_regmap_lookup_by_compatible(compatible); 1678c2ecf20Sopenharmony_ci if (IS_ERR(map)) 1688c2ecf20Sopenharmony_ci return PTR_ERR(map); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci f = devm_regmap_field_alloc(dev, map, data->channels[i].reset); 1718c2ecf20Sopenharmony_ci if (IS_ERR(f)) 1728c2ecf20Sopenharmony_ci return PTR_ERR(f); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci rc->channels[i].reset = f; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci if (!data->wait_for_ack) 1778c2ecf20Sopenharmony_ci continue; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci f = devm_regmap_field_alloc(dev, map, data->channels[i].ack); 1808c2ecf20Sopenharmony_ci if (IS_ERR(f)) 1818c2ecf20Sopenharmony_ci return PTR_ERR(f); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci rc->channels[i].ack = f; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci err = reset_controller_register(&rc->rst); 1878c2ecf20Sopenharmony_ci if (!err) 1888c2ecf20Sopenharmony_ci dev_info(dev, "registered\n"); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return err; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciint syscfg_reset_probe(struct platform_device *pdev) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct device *dev = pdev ? &pdev->dev : NULL; 1968c2ecf20Sopenharmony_ci const struct of_device_id *match; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (!dev || !dev->driver) 1998c2ecf20Sopenharmony_ci return -ENODEV; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci match = of_match_device(dev->driver->of_match_table, dev); 2028c2ecf20Sopenharmony_ci if (!match || !match->data) 2038c2ecf20Sopenharmony_ci return -EINVAL; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return syscfg_reset_controller_register(dev, match->data); 2068c2ecf20Sopenharmony_ci} 207