162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 STMicroelectronics Limited 462306a36Sopenharmony_ci * Author: Stephen Gallimore <stephen.gallimore@st.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Inspired by mach-imx/src.c 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/platform_device.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/of_device.h> 1462306a36Sopenharmony_ci#include <linux/regmap.h> 1562306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "reset-syscfg.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * struct syscfg_reset_channel - Reset channel regmap configuration 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * @reset: regmap field for the channel's reset bit. 2362306a36Sopenharmony_ci * @ack: regmap field for the channel's ack bit (optional). 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_cistruct syscfg_reset_channel { 2662306a36Sopenharmony_ci struct regmap_field *reset; 2762306a36Sopenharmony_ci struct regmap_field *ack; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/** 3162306a36Sopenharmony_ci * struct syscfg_reset_controller - A reset controller which groups together 3262306a36Sopenharmony_ci * a set of related reset bits, which may be located in different system 3362306a36Sopenharmony_ci * configuration registers. 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * @rst: base reset controller structure. 3662306a36Sopenharmony_ci * @active_low: are the resets in this controller active low, i.e. clearing 3762306a36Sopenharmony_ci * the reset bit puts the hardware into reset. 3862306a36Sopenharmony_ci * @channels: An array of reset channels for this controller. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_cistruct syscfg_reset_controller { 4162306a36Sopenharmony_ci struct reset_controller_dev rst; 4262306a36Sopenharmony_ci bool active_low; 4362306a36Sopenharmony_ci struct syscfg_reset_channel *channels; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define to_syscfg_reset_controller(_rst) \ 4762306a36Sopenharmony_ci container_of(_rst, struct syscfg_reset_controller, rst) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int syscfg_reset_program_hw(struct reset_controller_dev *rcdev, 5062306a36Sopenharmony_ci unsigned long idx, int assert) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev); 5362306a36Sopenharmony_ci const struct syscfg_reset_channel *ch; 5462306a36Sopenharmony_ci u32 ctrl_val = rst->active_low ? !assert : !!assert; 5562306a36Sopenharmony_ci int err; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (idx >= rcdev->nr_resets) 5862306a36Sopenharmony_ci return -EINVAL; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci ch = &rst->channels[idx]; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci err = regmap_field_write(ch->reset, ctrl_val); 6362306a36Sopenharmony_ci if (err) 6462306a36Sopenharmony_ci return err; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (ch->ack) { 6762306a36Sopenharmony_ci u32 ack_val; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci err = regmap_field_read_poll_timeout(ch->ack, ack_val, (ack_val == ctrl_val), 7062306a36Sopenharmony_ci 100, USEC_PER_SEC); 7162306a36Sopenharmony_ci if (err) 7262306a36Sopenharmony_ci return err; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int syscfg_reset_assert(struct reset_controller_dev *rcdev, 7962306a36Sopenharmony_ci unsigned long idx) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci return syscfg_reset_program_hw(rcdev, idx, true); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int syscfg_reset_deassert(struct reset_controller_dev *rcdev, 8562306a36Sopenharmony_ci unsigned long idx) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci return syscfg_reset_program_hw(rcdev, idx, false); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int syscfg_reset_dev(struct reset_controller_dev *rcdev, 9162306a36Sopenharmony_ci unsigned long idx) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci int err; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci err = syscfg_reset_assert(rcdev, idx); 9662306a36Sopenharmony_ci if (err) 9762306a36Sopenharmony_ci return err; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return syscfg_reset_deassert(rcdev, idx); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int syscfg_reset_status(struct reset_controller_dev *rcdev, 10362306a36Sopenharmony_ci unsigned long idx) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct syscfg_reset_controller *rst = to_syscfg_reset_controller(rcdev); 10662306a36Sopenharmony_ci const struct syscfg_reset_channel *ch; 10762306a36Sopenharmony_ci u32 ret_val = 0; 10862306a36Sopenharmony_ci int err; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (idx >= rcdev->nr_resets) 11162306a36Sopenharmony_ci return -EINVAL; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci ch = &rst->channels[idx]; 11462306a36Sopenharmony_ci if (ch->ack) 11562306a36Sopenharmony_ci err = regmap_field_read(ch->ack, &ret_val); 11662306a36Sopenharmony_ci else 11762306a36Sopenharmony_ci err = regmap_field_read(ch->reset, &ret_val); 11862306a36Sopenharmony_ci if (err) 11962306a36Sopenharmony_ci return err; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return rst->active_low ? !ret_val : !!ret_val; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic const struct reset_control_ops syscfg_reset_ops = { 12562306a36Sopenharmony_ci .reset = syscfg_reset_dev, 12662306a36Sopenharmony_ci .assert = syscfg_reset_assert, 12762306a36Sopenharmony_ci .deassert = syscfg_reset_deassert, 12862306a36Sopenharmony_ci .status = syscfg_reset_status, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic int syscfg_reset_controller_register(struct device *dev, 13262306a36Sopenharmony_ci const struct syscfg_reset_controller_data *data) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct syscfg_reset_controller *rc; 13562306a36Sopenharmony_ci int i, err; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL); 13862306a36Sopenharmony_ci if (!rc) 13962306a36Sopenharmony_ci return -ENOMEM; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci rc->channels = devm_kcalloc(dev, data->nr_channels, 14262306a36Sopenharmony_ci sizeof(*rc->channels), GFP_KERNEL); 14362306a36Sopenharmony_ci if (!rc->channels) 14462306a36Sopenharmony_ci return -ENOMEM; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci rc->rst.ops = &syscfg_reset_ops; 14762306a36Sopenharmony_ci rc->rst.of_node = dev->of_node; 14862306a36Sopenharmony_ci rc->rst.nr_resets = data->nr_channels; 14962306a36Sopenharmony_ci rc->active_low = data->active_low; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci for (i = 0; i < data->nr_channels; i++) { 15262306a36Sopenharmony_ci struct regmap *map; 15362306a36Sopenharmony_ci struct regmap_field *f; 15462306a36Sopenharmony_ci const char *compatible = data->channels[i].compatible; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci map = syscon_regmap_lookup_by_compatible(compatible); 15762306a36Sopenharmony_ci if (IS_ERR(map)) 15862306a36Sopenharmony_ci return PTR_ERR(map); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci f = devm_regmap_field_alloc(dev, map, data->channels[i].reset); 16162306a36Sopenharmony_ci if (IS_ERR(f)) 16262306a36Sopenharmony_ci return PTR_ERR(f); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci rc->channels[i].reset = f; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (!data->wait_for_ack) 16762306a36Sopenharmony_ci continue; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci f = devm_regmap_field_alloc(dev, map, data->channels[i].ack); 17062306a36Sopenharmony_ci if (IS_ERR(f)) 17162306a36Sopenharmony_ci return PTR_ERR(f); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci rc->channels[i].ack = f; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci err = reset_controller_register(&rc->rst); 17762306a36Sopenharmony_ci if (!err) 17862306a36Sopenharmony_ci dev_info(dev, "registered\n"); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return err; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ciint syscfg_reset_probe(struct platform_device *pdev) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct device *dev = pdev ? &pdev->dev : NULL; 18662306a36Sopenharmony_ci const struct of_device_id *match; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (!dev || !dev->driver) 18962306a36Sopenharmony_ci return -ENODEV; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci match = of_match_device(dev->driver->of_match_table, dev); 19262306a36Sopenharmony_ci if (!match || !match->data) 19362306a36Sopenharmony_ci return -EINVAL; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return syscfg_reset_controller_register(dev, match->data); 19662306a36Sopenharmony_ci} 197