162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (C) 2018-2020 Broadcom */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/device.h>
562306a36Sopenharmony_ci#include <linux/iopoll.h>
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/of.h>
862306a36Sopenharmony_ci#include <linux/platform_device.h>
962306a36Sopenharmony_ci#include <linux/reset-controller.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define BRCM_RESCAL_START	0x0
1262306a36Sopenharmony_ci#define  BRCM_RESCAL_START_BIT	BIT(0)
1362306a36Sopenharmony_ci#define BRCM_RESCAL_CTRL	0x4
1462306a36Sopenharmony_ci#define BRCM_RESCAL_STATUS	0x8
1562306a36Sopenharmony_ci#define  BRCM_RESCAL_STATUS_BIT	BIT(0)
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistruct brcm_rescal_reset {
1862306a36Sopenharmony_ci	void __iomem *base;
1962306a36Sopenharmony_ci	struct device *dev;
2062306a36Sopenharmony_ci	struct reset_controller_dev rcdev;
2162306a36Sopenharmony_ci};
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int brcm_rescal_reset_set(struct reset_controller_dev *rcdev,
2462306a36Sopenharmony_ci				 unsigned long id)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct brcm_rescal_reset *data =
2762306a36Sopenharmony_ci		container_of(rcdev, struct brcm_rescal_reset, rcdev);
2862306a36Sopenharmony_ci	void __iomem *base = data->base;
2962306a36Sopenharmony_ci	u32 reg;
3062306a36Sopenharmony_ci	int ret;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	reg = readl(base + BRCM_RESCAL_START);
3362306a36Sopenharmony_ci	writel(reg | BRCM_RESCAL_START_BIT, base + BRCM_RESCAL_START);
3462306a36Sopenharmony_ci	reg = readl(base + BRCM_RESCAL_START);
3562306a36Sopenharmony_ci	if (!(reg & BRCM_RESCAL_START_BIT)) {
3662306a36Sopenharmony_ci		dev_err(data->dev, "failed to start SATA/PCIe rescal\n");
3762306a36Sopenharmony_ci		return -EIO;
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	ret = readl_poll_timeout(base + BRCM_RESCAL_STATUS, reg,
4162306a36Sopenharmony_ci				 (reg & BRCM_RESCAL_STATUS_BIT), 100, 1000);
4262306a36Sopenharmony_ci	if (ret) {
4362306a36Sopenharmony_ci		dev_err(data->dev, "time out on SATA/PCIe rescal\n");
4462306a36Sopenharmony_ci		return ret;
4562306a36Sopenharmony_ci	}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	reg = readl(base + BRCM_RESCAL_START);
4862306a36Sopenharmony_ci	writel(reg & ~BRCM_RESCAL_START_BIT, base + BRCM_RESCAL_START);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	dev_dbg(data->dev, "SATA/PCIe rescal success\n");
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return 0;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int brcm_rescal_reset_xlate(struct reset_controller_dev *rcdev,
5662306a36Sopenharmony_ci				   const struct of_phandle_args *reset_spec)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	/* This is needed if #reset-cells == 0. */
5962306a36Sopenharmony_ci	return 0;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const struct reset_control_ops brcm_rescal_reset_ops = {
6362306a36Sopenharmony_ci	.reset = brcm_rescal_reset_set,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int brcm_rescal_reset_probe(struct platform_device *pdev)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct brcm_rescal_reset *data;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
7162306a36Sopenharmony_ci	if (!data)
7262306a36Sopenharmony_ci		return -ENOMEM;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	data->base = devm_platform_ioremap_resource(pdev, 0);
7562306a36Sopenharmony_ci	if (IS_ERR(data->base))
7662306a36Sopenharmony_ci		return PTR_ERR(data->base);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	data->rcdev.owner = THIS_MODULE;
7962306a36Sopenharmony_ci	data->rcdev.nr_resets = 1;
8062306a36Sopenharmony_ci	data->rcdev.ops = &brcm_rescal_reset_ops;
8162306a36Sopenharmony_ci	data->rcdev.of_node = pdev->dev.of_node;
8262306a36Sopenharmony_ci	data->rcdev.of_xlate = brcm_rescal_reset_xlate;
8362306a36Sopenharmony_ci	data->dev = &pdev->dev;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return devm_reset_controller_register(&pdev->dev, &data->rcdev);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic const struct of_device_id brcm_rescal_reset_of_match[] = {
8962306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7216-pcie-sata-rescal" },
9062306a36Sopenharmony_ci	{ },
9162306a36Sopenharmony_ci};
9262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, brcm_rescal_reset_of_match);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic struct platform_driver brcm_rescal_reset_driver = {
9562306a36Sopenharmony_ci	.probe = brcm_rescal_reset_probe,
9662306a36Sopenharmony_ci	.driver = {
9762306a36Sopenharmony_ci		.name	= "brcm-rescal-reset",
9862306a36Sopenharmony_ci		.of_match_table	= brcm_rescal_reset_of_match,
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_cimodule_platform_driver(brcm_rescal_reset_driver);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom");
10462306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom SATA/PCIe rescal reset controller");
10562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
106