162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * BCM6345 Reset Controller Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2020 Álvaro Fernández Rojas <noltari@gmail.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/delay.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/io.h>
1162306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1262306a36Sopenharmony_ci#include <linux/platform_device.h>
1362306a36Sopenharmony_ci#include <linux/reset-controller.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define BCM6345_RESET_NUM		32
1662306a36Sopenharmony_ci#define BCM6345_RESET_SLEEP_MIN_US	10000
1762306a36Sopenharmony_ci#define BCM6345_RESET_SLEEP_MAX_US	20000
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct bcm6345_reset {
2062306a36Sopenharmony_ci	struct reset_controller_dev rcdev;
2162306a36Sopenharmony_ci	void __iomem *base;
2262306a36Sopenharmony_ci	spinlock_t lock;
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic inline struct bcm6345_reset *
2662306a36Sopenharmony_cito_bcm6345_reset(struct reset_controller_dev *rcdev)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	return container_of(rcdev, struct bcm6345_reset, rcdev);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int bcm6345_reset_update(struct reset_controller_dev *rcdev,
3262306a36Sopenharmony_ci				unsigned long id, bool assert)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct bcm6345_reset *bcm6345_reset = to_bcm6345_reset(rcdev);
3562306a36Sopenharmony_ci	unsigned long flags;
3662306a36Sopenharmony_ci	uint32_t val;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	spin_lock_irqsave(&bcm6345_reset->lock, flags);
3962306a36Sopenharmony_ci	val = __raw_readl(bcm6345_reset->base);
4062306a36Sopenharmony_ci	if (assert)
4162306a36Sopenharmony_ci		val &= ~BIT(id);
4262306a36Sopenharmony_ci	else
4362306a36Sopenharmony_ci		val |= BIT(id);
4462306a36Sopenharmony_ci	__raw_writel(val, bcm6345_reset->base);
4562306a36Sopenharmony_ci	spin_unlock_irqrestore(&bcm6345_reset->lock, flags);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int bcm6345_reset_assert(struct reset_controller_dev *rcdev,
5162306a36Sopenharmony_ci				unsigned long id)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	return bcm6345_reset_update(rcdev, id, true);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int bcm6345_reset_deassert(struct reset_controller_dev *rcdev,
5762306a36Sopenharmony_ci				  unsigned long id)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	return bcm6345_reset_update(rcdev, id, false);
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int bcm6345_reset_reset(struct reset_controller_dev *rcdev,
6362306a36Sopenharmony_ci			       unsigned long id)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	bcm6345_reset_update(rcdev, id, true);
6662306a36Sopenharmony_ci	usleep_range(BCM6345_RESET_SLEEP_MIN_US,
6762306a36Sopenharmony_ci		     BCM6345_RESET_SLEEP_MAX_US);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	bcm6345_reset_update(rcdev, id, false);
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * Ensure component is taken out reset state by sleeping also after
7262306a36Sopenharmony_ci	 * deasserting the reset. Otherwise, the component may not be ready
7362306a36Sopenharmony_ci	 * for operation.
7462306a36Sopenharmony_ci	 */
7562306a36Sopenharmony_ci	usleep_range(BCM6345_RESET_SLEEP_MIN_US,
7662306a36Sopenharmony_ci		     BCM6345_RESET_SLEEP_MAX_US);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic int bcm6345_reset_status(struct reset_controller_dev *rcdev,
8262306a36Sopenharmony_ci				unsigned long id)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct bcm6345_reset *bcm6345_reset = to_bcm6345_reset(rcdev);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return !(__raw_readl(bcm6345_reset->base) & BIT(id));
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic const struct reset_control_ops bcm6345_reset_ops = {
9062306a36Sopenharmony_ci	.assert = bcm6345_reset_assert,
9162306a36Sopenharmony_ci	.deassert = bcm6345_reset_deassert,
9262306a36Sopenharmony_ci	.reset = bcm6345_reset_reset,
9362306a36Sopenharmony_ci	.status = bcm6345_reset_status,
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int bcm6345_reset_probe(struct platform_device *pdev)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct bcm6345_reset *bcm6345_reset;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	bcm6345_reset = devm_kzalloc(&pdev->dev,
10162306a36Sopenharmony_ci				     sizeof(*bcm6345_reset), GFP_KERNEL);
10262306a36Sopenharmony_ci	if (!bcm6345_reset)
10362306a36Sopenharmony_ci		return -ENOMEM;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	bcm6345_reset->base = devm_platform_ioremap_resource(pdev, 0);
10662306a36Sopenharmony_ci	if (IS_ERR(bcm6345_reset->base))
10762306a36Sopenharmony_ci		return PTR_ERR(bcm6345_reset->base);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	spin_lock_init(&bcm6345_reset->lock);
11062306a36Sopenharmony_ci	bcm6345_reset->rcdev.ops = &bcm6345_reset_ops;
11162306a36Sopenharmony_ci	bcm6345_reset->rcdev.owner = THIS_MODULE;
11262306a36Sopenharmony_ci	bcm6345_reset->rcdev.of_node = pdev->dev.of_node;
11362306a36Sopenharmony_ci	bcm6345_reset->rcdev.of_reset_n_cells = 1;
11462306a36Sopenharmony_ci	bcm6345_reset->rcdev.nr_resets = BCM6345_RESET_NUM;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return devm_reset_controller_register(&pdev->dev,
11762306a36Sopenharmony_ci					      &bcm6345_reset->rcdev);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic const struct of_device_id bcm6345_reset_of_match[] = {
12162306a36Sopenharmony_ci	{ .compatible = "brcm,bcm6345-reset" },
12262306a36Sopenharmony_ci	{ /* sentinel */ },
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic struct platform_driver bcm6345_reset_driver = {
12662306a36Sopenharmony_ci	.probe = bcm6345_reset_probe,
12762306a36Sopenharmony_ci	.driver	= {
12862306a36Sopenharmony_ci		.name = "bcm6345-reset",
12962306a36Sopenharmony_ci		.of_match_table = bcm6345_reset_of_match,
13062306a36Sopenharmony_ci		.suppress_bind_attrs = true,
13162306a36Sopenharmony_ci	},
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_cibuiltin_platform_driver(bcm6345_reset_driver);
134