162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2016-2017 Linaro Ltd.
462306a36Sopenharmony_ci * Copyright (c) 2016-2017 HiSilicon Technologies Co., Ltd.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/platform_device.h>
1162306a36Sopenharmony_ci#include <linux/regmap.h>
1262306a36Sopenharmony_ci#include <linux/reset-controller.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct hi3660_reset_controller {
1562306a36Sopenharmony_ci	struct reset_controller_dev rst;
1662306a36Sopenharmony_ci	struct regmap *map;
1762306a36Sopenharmony_ci};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define to_hi3660_reset_controller(_rst) \
2062306a36Sopenharmony_ci	container_of(_rst, struct hi3660_reset_controller, rst)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic int hi3660_reset_program_hw(struct reset_controller_dev *rcdev,
2362306a36Sopenharmony_ci				   unsigned long idx, bool assert)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	struct hi3660_reset_controller *rc = to_hi3660_reset_controller(rcdev);
2662306a36Sopenharmony_ci	unsigned int offset = idx >> 8;
2762306a36Sopenharmony_ci	unsigned int mask = BIT(idx & 0x1f);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	if (assert)
3062306a36Sopenharmony_ci		return regmap_write(rc->map, offset, mask);
3162306a36Sopenharmony_ci	else
3262306a36Sopenharmony_ci		return regmap_write(rc->map, offset + 4, mask);
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int hi3660_reset_assert(struct reset_controller_dev *rcdev,
3662306a36Sopenharmony_ci			       unsigned long idx)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	return hi3660_reset_program_hw(rcdev, idx, true);
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int hi3660_reset_deassert(struct reset_controller_dev *rcdev,
4262306a36Sopenharmony_ci				 unsigned long idx)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	return hi3660_reset_program_hw(rcdev, idx, false);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int hi3660_reset_dev(struct reset_controller_dev *rcdev,
4862306a36Sopenharmony_ci			    unsigned long idx)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	int err;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	err = hi3660_reset_assert(rcdev, idx);
5362306a36Sopenharmony_ci	if (err)
5462306a36Sopenharmony_ci		return err;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return hi3660_reset_deassert(rcdev, idx);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic const struct reset_control_ops hi3660_reset_ops = {
6062306a36Sopenharmony_ci	.reset    = hi3660_reset_dev,
6162306a36Sopenharmony_ci	.assert   = hi3660_reset_assert,
6262306a36Sopenharmony_ci	.deassert = hi3660_reset_deassert,
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int hi3660_reset_xlate(struct reset_controller_dev *rcdev,
6662306a36Sopenharmony_ci			      const struct of_phandle_args *reset_spec)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	unsigned int offset, bit;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	offset = reset_spec->args[0];
7162306a36Sopenharmony_ci	bit = reset_spec->args[1];
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return (offset << 8) | bit;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int hi3660_reset_probe(struct platform_device *pdev)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct hi3660_reset_controller *rc;
7962306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
8062306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	rc = devm_kzalloc(dev, sizeof(*rc), GFP_KERNEL);
8362306a36Sopenharmony_ci	if (!rc)
8462306a36Sopenharmony_ci		return -ENOMEM;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	rc->map = syscon_regmap_lookup_by_phandle(np, "hisilicon,rst-syscon");
8762306a36Sopenharmony_ci	if (rc->map == ERR_PTR(-ENODEV)) {
8862306a36Sopenharmony_ci		/* fall back to the deprecated compatible */
8962306a36Sopenharmony_ci		rc->map = syscon_regmap_lookup_by_phandle(np,
9062306a36Sopenharmony_ci							  "hisi,rst-syscon");
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	if (IS_ERR(rc->map)) {
9362306a36Sopenharmony_ci		return dev_err_probe(dev, PTR_ERR(rc->map),
9462306a36Sopenharmony_ci			"failed to get hisilicon,rst-syscon\n");
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	rc->rst.ops = &hi3660_reset_ops,
9862306a36Sopenharmony_ci	rc->rst.of_node = np;
9962306a36Sopenharmony_ci	rc->rst.of_reset_n_cells = 2;
10062306a36Sopenharmony_ci	rc->rst.of_xlate = hi3660_reset_xlate;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	return reset_controller_register(&rc->rst);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic const struct of_device_id hi3660_reset_match[] = {
10662306a36Sopenharmony_ci	{ .compatible = "hisilicon,hi3660-reset", },
10762306a36Sopenharmony_ci	{},
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, hi3660_reset_match);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic struct platform_driver hi3660_reset_driver = {
11262306a36Sopenharmony_ci	.probe = hi3660_reset_probe,
11362306a36Sopenharmony_ci	.driver = {
11462306a36Sopenharmony_ci		.name = "hi3660-reset",
11562306a36Sopenharmony_ci		.of_match_table = hi3660_reset_match,
11662306a36Sopenharmony_ci	},
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int __init hi3660_reset_init(void)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	return platform_driver_register(&hi3660_reset_driver);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ciarch_initcall(hi3660_reset_init);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
12662306a36Sopenharmony_ciMODULE_ALIAS("platform:hi3660-reset");
12762306a36Sopenharmony_ciMODULE_DESCRIPTION("HiSilicon Hi3660 Reset Driver");
128