18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (C) 2014 Marvell Technology Group Ltd.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Marvell Berlin reset driver
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Antoine Tenart <antoine.tenart@free-electrons.com>
78c2ecf20Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
108c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any
118c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
178c2ecf20Sopenharmony_ci#include <linux/init.h>
188c2ecf20Sopenharmony_ci#include <linux/of.h>
198c2ecf20Sopenharmony_ci#include <linux/of_address.h>
208c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
218c2ecf20Sopenharmony_ci#include <linux/regmap.h>
228c2ecf20Sopenharmony_ci#include <linux/reset-controller.h>
238c2ecf20Sopenharmony_ci#include <linux/slab.h>
248c2ecf20Sopenharmony_ci#include <linux/types.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define BERLIN_MAX_RESETS	32
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define to_berlin_reset_priv(p)		\
298c2ecf20Sopenharmony_ci	container_of((p), struct berlin_reset_priv, rcdev)
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct berlin_reset_priv {
328c2ecf20Sopenharmony_ci	struct regmap			*regmap;
338c2ecf20Sopenharmony_ci	struct reset_controller_dev	rcdev;
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int berlin_reset_reset(struct reset_controller_dev *rcdev,
378c2ecf20Sopenharmony_ci			      unsigned long id)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	struct berlin_reset_priv *priv = to_berlin_reset_priv(rcdev);
408c2ecf20Sopenharmony_ci	int offset = id >> 8;
418c2ecf20Sopenharmony_ci	int mask = BIT(id & 0x1f);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	regmap_write(priv->regmap, offset, mask);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/* let the reset be effective */
468c2ecf20Sopenharmony_ci	udelay(10);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return 0;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic const struct reset_control_ops berlin_reset_ops = {
528c2ecf20Sopenharmony_ci	.reset	= berlin_reset_reset,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int berlin_reset_xlate(struct reset_controller_dev *rcdev,
568c2ecf20Sopenharmony_ci			      const struct of_phandle_args *reset_spec)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	unsigned offset, bit;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	offset = reset_spec->args[0];
618c2ecf20Sopenharmony_ci	bit = reset_spec->args[1];
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	if (bit >= BERLIN_MAX_RESETS)
648c2ecf20Sopenharmony_ci		return -EINVAL;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return (offset << 8) | bit;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int berlin2_reset_probe(struct platform_device *pdev)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct device_node *parent_np = of_get_parent(pdev->dev.of_node);
728c2ecf20Sopenharmony_ci	struct berlin_reset_priv *priv;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
758c2ecf20Sopenharmony_ci	if (!priv)
768c2ecf20Sopenharmony_ci		return -ENOMEM;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	priv->regmap = syscon_node_to_regmap(parent_np);
798c2ecf20Sopenharmony_ci	of_node_put(parent_np);
808c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regmap))
818c2ecf20Sopenharmony_ci		return PTR_ERR(priv->regmap);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	priv->rcdev.owner = THIS_MODULE;
848c2ecf20Sopenharmony_ci	priv->rcdev.ops = &berlin_reset_ops;
858c2ecf20Sopenharmony_ci	priv->rcdev.of_node = pdev->dev.of_node;
868c2ecf20Sopenharmony_ci	priv->rcdev.of_reset_n_cells = 2;
878c2ecf20Sopenharmony_ci	priv->rcdev.of_xlate = berlin_reset_xlate;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return reset_controller_register(&priv->rcdev);
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic const struct of_device_id berlin_reset_dt_match[] = {
938c2ecf20Sopenharmony_ci	{ .compatible = "marvell,berlin2-reset" },
948c2ecf20Sopenharmony_ci	{ },
958c2ecf20Sopenharmony_ci};
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic struct platform_driver berlin_reset_driver = {
988c2ecf20Sopenharmony_ci	.probe	= berlin2_reset_probe,
998c2ecf20Sopenharmony_ci	.driver	= {
1008c2ecf20Sopenharmony_ci		.name = "berlin2-reset",
1018c2ecf20Sopenharmony_ci		.of_match_table = berlin_reset_dt_match,
1028c2ecf20Sopenharmony_ci	},
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_cibuiltin_platform_driver(berlin_reset_driver);
105