162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *   Serge Semin <Sergey.Semin@baikalelectronics.ru>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Baikal-T1 CCU Resets interface driver
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) "bt1-ccu-rst: " fmt
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/bits.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/kernel.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include <linux/printk.h>
1862306a36Sopenharmony_ci#include <linux/regmap.h>
1962306a36Sopenharmony_ci#include <linux/reset-controller.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include <dt-bindings/reset/bt1-ccu.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "ccu-rst.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define CCU_AXI_MAIN_BASE		0x030
2762306a36Sopenharmony_ci#define CCU_AXI_DDR_BASE		0x034
2862306a36Sopenharmony_ci#define CCU_AXI_SATA_BASE		0x038
2962306a36Sopenharmony_ci#define CCU_AXI_GMAC0_BASE		0x03C
3062306a36Sopenharmony_ci#define CCU_AXI_GMAC1_BASE		0x040
3162306a36Sopenharmony_ci#define CCU_AXI_XGMAC_BASE		0x044
3262306a36Sopenharmony_ci#define CCU_AXI_PCIE_M_BASE		0x048
3362306a36Sopenharmony_ci#define CCU_AXI_PCIE_S_BASE		0x04C
3462306a36Sopenharmony_ci#define CCU_AXI_USB_BASE		0x050
3562306a36Sopenharmony_ci#define CCU_AXI_HWA_BASE		0x054
3662306a36Sopenharmony_ci#define CCU_AXI_SRAM_BASE		0x058
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define CCU_SYS_DDR_BASE		0x02c
3962306a36Sopenharmony_ci#define CCU_SYS_SATA_REF_BASE		0x060
4062306a36Sopenharmony_ci#define CCU_SYS_APB_BASE		0x064
4162306a36Sopenharmony_ci#define CCU_SYS_PCIE_BASE		0x144
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define CCU_RST_DELAY_US		1
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define CCU_RST_TRIG(_base, _ofs)		\
4662306a36Sopenharmony_ci	{					\
4762306a36Sopenharmony_ci		.type = CCU_RST_TRIG,		\
4862306a36Sopenharmony_ci		.base = _base,			\
4962306a36Sopenharmony_ci		.mask = BIT(_ofs),		\
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define CCU_RST_DIR(_base, _ofs)		\
5362306a36Sopenharmony_ci	{					\
5462306a36Sopenharmony_ci		.type = CCU_RST_DIR,		\
5562306a36Sopenharmony_ci		.base = _base,			\
5662306a36Sopenharmony_ci		.mask = BIT(_ofs),		\
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct ccu_rst_info {
6062306a36Sopenharmony_ci	enum ccu_rst_type type;
6162306a36Sopenharmony_ci	unsigned int base;
6262306a36Sopenharmony_ci	unsigned int mask;
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * Each AXI-bus clock divider is equipped with the corresponding clock-consumer
6762306a36Sopenharmony_ci * domain reset (it's self-deasserted reset control).
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_cistatic const struct ccu_rst_info axi_rst_info[] = {
7062306a36Sopenharmony_ci	[CCU_AXI_MAIN_RST] = CCU_RST_TRIG(CCU_AXI_MAIN_BASE, 1),
7162306a36Sopenharmony_ci	[CCU_AXI_DDR_RST] = CCU_RST_TRIG(CCU_AXI_DDR_BASE, 1),
7262306a36Sopenharmony_ci	[CCU_AXI_SATA_RST] = CCU_RST_TRIG(CCU_AXI_SATA_BASE, 1),
7362306a36Sopenharmony_ci	[CCU_AXI_GMAC0_RST] = CCU_RST_TRIG(CCU_AXI_GMAC0_BASE, 1),
7462306a36Sopenharmony_ci	[CCU_AXI_GMAC1_RST] = CCU_RST_TRIG(CCU_AXI_GMAC1_BASE, 1),
7562306a36Sopenharmony_ci	[CCU_AXI_XGMAC_RST] = CCU_RST_TRIG(CCU_AXI_XGMAC_BASE, 1),
7662306a36Sopenharmony_ci	[CCU_AXI_PCIE_M_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_M_BASE, 1),
7762306a36Sopenharmony_ci	[CCU_AXI_PCIE_S_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_S_BASE, 1),
7862306a36Sopenharmony_ci	[CCU_AXI_USB_RST] = CCU_RST_TRIG(CCU_AXI_USB_BASE, 1),
7962306a36Sopenharmony_ci	[CCU_AXI_HWA_RST] = CCU_RST_TRIG(CCU_AXI_HWA_BASE, 1),
8062306a36Sopenharmony_ci	[CCU_AXI_SRAM_RST] = CCU_RST_TRIG(CCU_AXI_SRAM_BASE, 1),
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/*
8462306a36Sopenharmony_ci * SATA reference clock domain and APB-bus domain are connected with the
8562306a36Sopenharmony_ci * sefl-deasserted reset control, which can be activated via the corresponding
8662306a36Sopenharmony_ci * clock divider register. DDR and PCIe sub-domains can be reset with directly
8762306a36Sopenharmony_ci * controlled reset signals. Resetting the DDR controller though won't end up
8862306a36Sopenharmony_ci * well while the Linux kernel is working.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic const struct ccu_rst_info sys_rst_info[] = {
9162306a36Sopenharmony_ci	[CCU_SYS_SATA_REF_RST] = CCU_RST_TRIG(CCU_SYS_SATA_REF_BASE, 1),
9262306a36Sopenharmony_ci	[CCU_SYS_APB_RST] = CCU_RST_TRIG(CCU_SYS_APB_BASE, 1),
9362306a36Sopenharmony_ci	[CCU_SYS_DDR_FULL_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 1),
9462306a36Sopenharmony_ci	[CCU_SYS_DDR_INIT_RST] = CCU_RST_DIR(CCU_SYS_DDR_BASE, 2),
9562306a36Sopenharmony_ci	[CCU_SYS_PCIE_PCS_PHY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 0),
9662306a36Sopenharmony_ci	[CCU_SYS_PCIE_PIPE0_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 4),
9762306a36Sopenharmony_ci	[CCU_SYS_PCIE_CORE_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 8),
9862306a36Sopenharmony_ci	[CCU_SYS_PCIE_PWR_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 9),
9962306a36Sopenharmony_ci	[CCU_SYS_PCIE_STICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 10),
10062306a36Sopenharmony_ci	[CCU_SYS_PCIE_NSTICKY_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 11),
10162306a36Sopenharmony_ci	[CCU_SYS_PCIE_HOT_RST] = CCU_RST_DIR(CCU_SYS_PCIE_BASE, 12),
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic int ccu_rst_reset(struct reset_controller_dev *rcdev, unsigned long idx)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct ccu_rst *rst = to_ccu_rst(rcdev);
10762306a36Sopenharmony_ci	const struct ccu_rst_info *info = &rst->rsts_info[idx];
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (info->type != CCU_RST_TRIG)
11062306a36Sopenharmony_ci		return -EOPNOTSUPP;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	regmap_update_bits(rst->sys_regs, info->base, info->mask, info->mask);
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* The next delay must be enough to cover all the resets. */
11562306a36Sopenharmony_ci	udelay(CCU_RST_DELAY_US);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int ccu_rst_set(struct reset_controller_dev *rcdev,
12162306a36Sopenharmony_ci		       unsigned long idx, bool high)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct ccu_rst *rst = to_ccu_rst(rcdev);
12462306a36Sopenharmony_ci	const struct ccu_rst_info *info = &rst->rsts_info[idx];
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (info->type != CCU_RST_DIR)
12762306a36Sopenharmony_ci		return high ? -EOPNOTSUPP : 0;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	return regmap_update_bits(rst->sys_regs, info->base,
13062306a36Sopenharmony_ci				  info->mask, high ? info->mask : 0);
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int ccu_rst_assert(struct reset_controller_dev *rcdev,
13462306a36Sopenharmony_ci			  unsigned long idx)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	return ccu_rst_set(rcdev, idx, true);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int ccu_rst_deassert(struct reset_controller_dev *rcdev,
14062306a36Sopenharmony_ci			    unsigned long idx)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	return ccu_rst_set(rcdev, idx, false);
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int ccu_rst_status(struct reset_controller_dev *rcdev,
14662306a36Sopenharmony_ci			  unsigned long idx)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct ccu_rst *rst = to_ccu_rst(rcdev);
14962306a36Sopenharmony_ci	const struct ccu_rst_info *info = &rst->rsts_info[idx];
15062306a36Sopenharmony_ci	u32 val;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (info->type != CCU_RST_DIR)
15362306a36Sopenharmony_ci		return -EOPNOTSUPP;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	regmap_read(rst->sys_regs, info->base, &val);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return !!(val & info->mask);
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic const struct reset_control_ops ccu_rst_ops = {
16162306a36Sopenharmony_ci	.reset = ccu_rst_reset,
16262306a36Sopenharmony_ci	.assert = ccu_rst_assert,
16362306a36Sopenharmony_ci	.deassert = ccu_rst_deassert,
16462306a36Sopenharmony_ci	.status = ccu_rst_status,
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistruct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *rst_init)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	struct ccu_rst *rst;
17062306a36Sopenharmony_ci	int ret;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (!rst_init)
17362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	rst = kzalloc(sizeof(*rst), GFP_KERNEL);
17662306a36Sopenharmony_ci	if (!rst)
17762306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	rst->sys_regs = rst_init->sys_regs;
18062306a36Sopenharmony_ci	if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-axi")) {
18162306a36Sopenharmony_ci		rst->rcdev.nr_resets = ARRAY_SIZE(axi_rst_info);
18262306a36Sopenharmony_ci		rst->rsts_info = axi_rst_info;
18362306a36Sopenharmony_ci	} else if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-sys")) {
18462306a36Sopenharmony_ci		rst->rcdev.nr_resets = ARRAY_SIZE(sys_rst_info);
18562306a36Sopenharmony_ci		rst->rsts_info = sys_rst_info;
18662306a36Sopenharmony_ci	} else {
18762306a36Sopenharmony_ci		pr_err("Incompatible DT node '%s' specified\n",
18862306a36Sopenharmony_ci		       of_node_full_name(rst_init->np));
18962306a36Sopenharmony_ci		ret = -EINVAL;
19062306a36Sopenharmony_ci		goto err_kfree_rst;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	rst->rcdev.owner = THIS_MODULE;
19462306a36Sopenharmony_ci	rst->rcdev.ops = &ccu_rst_ops;
19562306a36Sopenharmony_ci	rst->rcdev.of_node = rst_init->np;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ret = reset_controller_register(&rst->rcdev);
19862306a36Sopenharmony_ci	if (ret) {
19962306a36Sopenharmony_ci		pr_err("Couldn't register '%s' reset controller\n",
20062306a36Sopenharmony_ci		       of_node_full_name(rst_init->np));
20162306a36Sopenharmony_ci		goto err_kfree_rst;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return rst;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cierr_kfree_rst:
20762306a36Sopenharmony_ci	kfree(rst);
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	return ERR_PTR(ret);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_civoid ccu_rst_hw_unregister(struct ccu_rst *rst)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	reset_controller_unregister(&rst->rcdev);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	kfree(rst);
21762306a36Sopenharmony_ci}
218