// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2021 BAIKAL ELECTRONICS, JSC * * Authors: * Serge Semin * * Baikal-T1 CCU Resets interface driver */ #define pr_fmt(fmt) "bt1-ccu-rst: " fmt #include #include #include #include #include #include #include #include #include #include "ccu-rst.h" #define CCU_AXI_MAIN_BASE 0x030 #define CCU_AXI_DDR_BASE 0x034 #define CCU_AXI_SATA_BASE 0x038 #define CCU_AXI_GMAC0_BASE 0x03C #define CCU_AXI_GMAC1_BASE 0x040 #define CCU_AXI_XGMAC_BASE 0x044 #define CCU_AXI_PCIE_M_BASE 0x048 #define CCU_AXI_PCIE_S_BASE 0x04C #define CCU_AXI_USB_BASE 0x050 #define CCU_AXI_HWA_BASE 0x054 #define CCU_AXI_SRAM_BASE 0x058 #define CCU_SYS_SATA_REF_BASE 0x060 #define CCU_SYS_APB_BASE 0x064 #define CCU_RST_DELAY_US 1 #define CCU_RST_TRIG(_base, _ofs) \ { \ .base = _base, \ .mask = BIT(_ofs), \ } struct ccu_rst_info { unsigned int base; unsigned int mask; }; /* * Each AXI-bus clock divider is equipped with the corresponding clock-consumer * domain reset (it's self-deasserted reset control). */ static const struct ccu_rst_info axi_rst_info[] = { [CCU_AXI_MAIN_RST] = CCU_RST_TRIG(CCU_AXI_MAIN_BASE, 1), [CCU_AXI_DDR_RST] = CCU_RST_TRIG(CCU_AXI_DDR_BASE, 1), [CCU_AXI_SATA_RST] = CCU_RST_TRIG(CCU_AXI_SATA_BASE, 1), [CCU_AXI_GMAC0_RST] = CCU_RST_TRIG(CCU_AXI_GMAC0_BASE, 1), [CCU_AXI_GMAC1_RST] = CCU_RST_TRIG(CCU_AXI_GMAC1_BASE, 1), [CCU_AXI_XGMAC_RST] = CCU_RST_TRIG(CCU_AXI_XGMAC_BASE, 1), [CCU_AXI_PCIE_M_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_M_BASE, 1), [CCU_AXI_PCIE_S_RST] = CCU_RST_TRIG(CCU_AXI_PCIE_S_BASE, 1), [CCU_AXI_USB_RST] = CCU_RST_TRIG(CCU_AXI_USB_BASE, 1), [CCU_AXI_HWA_RST] = CCU_RST_TRIG(CCU_AXI_HWA_BASE, 1), [CCU_AXI_SRAM_RST] = CCU_RST_TRIG(CCU_AXI_SRAM_BASE, 1), }; /* * SATA reference clock domain and APB-bus domain are connected with the * sefl-deasserted reset control, which can be activated via the corresponding * clock divider register. DDR and PCIe sub-domains can be reset with directly * controlled reset signals. Resetting the DDR controller though won't end up * well while the Linux kernel is working. */ static const struct ccu_rst_info sys_rst_info[] = { [CCU_SYS_SATA_REF_RST] = CCU_RST_TRIG(CCU_SYS_SATA_REF_BASE, 1), [CCU_SYS_APB_RST] = CCU_RST_TRIG(CCU_SYS_APB_BASE, 1), }; static int ccu_rst_reset(struct reset_controller_dev *rcdev, unsigned long idx) { struct ccu_rst *rst = to_ccu_rst(rcdev); const struct ccu_rst_info *info = &rst->rsts_info[idx]; regmap_update_bits(rst->sys_regs, info->base, info->mask, info->mask); /* The next delay must be enough to cover all the resets. */ udelay(CCU_RST_DELAY_US); return 0; } static const struct reset_control_ops ccu_rst_ops = { .reset = ccu_rst_reset, }; struct ccu_rst *ccu_rst_hw_register(const struct ccu_rst_init_data *rst_init) { struct ccu_rst *rst; int ret; if (!rst_init) return ERR_PTR(-EINVAL); rst = kzalloc(sizeof(*rst), GFP_KERNEL); if (!rst) return ERR_PTR(-ENOMEM); rst->sys_regs = rst_init->sys_regs; if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-axi")) { rst->rcdev.nr_resets = ARRAY_SIZE(axi_rst_info); rst->rsts_info = axi_rst_info; } else if (of_device_is_compatible(rst_init->np, "baikal,bt1-ccu-sys")) { rst->rcdev.nr_resets = ARRAY_SIZE(sys_rst_info); rst->rsts_info = sys_rst_info; } else { pr_err("Incompatible DT node '%s' specified\n", of_node_full_name(rst_init->np)); ret = -EINVAL; goto err_kfree_rst; } rst->rcdev.owner = THIS_MODULE; rst->rcdev.ops = &ccu_rst_ops; rst->rcdev.of_node = rst_init->np; ret = reset_controller_register(&rst->rcdev); if (ret) { pr_err("Couldn't register '%s' reset controller\n", of_node_full_name(rst_init->np)); goto err_kfree_rst; } return rst; err_kfree_rst: kfree(rst); return ERR_PTR(ret); } void ccu_rst_hw_unregister(struct ccu_rst *rst) { reset_controller_unregister(&rst->rcdev); kfree(rst); }