18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Serge Semin <Sergey.Semin@baikalelectronics.ru> 78c2ecf20Sopenharmony_ci * Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Baikal-T1 CCU PLL clocks driver 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "bt1-ccu-pll: " fmt 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/printk.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 188c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 198c2ecf20Sopenharmony_ci#include <linux/of.h> 208c2ecf20Sopenharmony_ci#include <linux/of_address.h> 218c2ecf20Sopenharmony_ci#include <linux/ioport.h> 228c2ecf20Sopenharmony_ci#include <linux/regmap.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <dt-bindings/clock/bt1-ccu.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "ccu-pll.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define CCU_CPU_PLL_BASE 0x000 298c2ecf20Sopenharmony_ci#define CCU_SATA_PLL_BASE 0x008 308c2ecf20Sopenharmony_ci#define CCU_DDR_PLL_BASE 0x010 318c2ecf20Sopenharmony_ci#define CCU_PCIE_PLL_BASE 0x018 328c2ecf20Sopenharmony_ci#define CCU_ETH_PLL_BASE 0x020 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags) \ 358c2ecf20Sopenharmony_ci { \ 368c2ecf20Sopenharmony_ci .id = _id, \ 378c2ecf20Sopenharmony_ci .name = _name, \ 388c2ecf20Sopenharmony_ci .parent_name = _pname, \ 398c2ecf20Sopenharmony_ci .base = _base, \ 408c2ecf20Sopenharmony_ci .flags = _flags \ 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define CCU_PLL_NUM ARRAY_SIZE(pll_info) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct ccu_pll_info { 468c2ecf20Sopenharmony_ci unsigned int id; 478c2ecf20Sopenharmony_ci const char *name; 488c2ecf20Sopenharmony_ci const char *parent_name; 498c2ecf20Sopenharmony_ci unsigned int base; 508c2ecf20Sopenharmony_ci unsigned long flags; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * Alas we have to mark all PLLs as critical. CPU and DDR PLLs are sources of 558c2ecf20Sopenharmony_ci * CPU cores and DDR controller reference clocks, due to which they obviously 568c2ecf20Sopenharmony_ci * shouldn't be ever gated. SATA and PCIe PLLs are the parents of APB-bus and 578c2ecf20Sopenharmony_ci * DDR controller AXI-bus clocks. If they are gated the system will be 588c2ecf20Sopenharmony_ci * unusable. Moreover disabling SATA and Ethernet PLLs causes automatic reset 598c2ecf20Sopenharmony_ci * of the corresponding subsystems. So until we aren't ready to re-initialize 608c2ecf20Sopenharmony_ci * all the devices consuming those PLLs, they will be marked as critical too. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistatic const struct ccu_pll_info pll_info[] = { 638c2ecf20Sopenharmony_ci CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE, 648c2ecf20Sopenharmony_ci CLK_IS_CRITICAL), 658c2ecf20Sopenharmony_ci CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE, 668c2ecf20Sopenharmony_ci CLK_IS_CRITICAL | CLK_SET_RATE_GATE), 678c2ecf20Sopenharmony_ci CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE, 688c2ecf20Sopenharmony_ci CLK_IS_CRITICAL | CLK_SET_RATE_GATE), 698c2ecf20Sopenharmony_ci CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE, 708c2ecf20Sopenharmony_ci CLK_IS_CRITICAL), 718c2ecf20Sopenharmony_ci CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE, 728c2ecf20Sopenharmony_ci CLK_IS_CRITICAL | CLK_SET_RATE_GATE) 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistruct ccu_pll_data { 768c2ecf20Sopenharmony_ci struct device_node *np; 778c2ecf20Sopenharmony_ci struct regmap *sys_regs; 788c2ecf20Sopenharmony_ci struct ccu_pll *plls[CCU_PLL_NUM]; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data, 828c2ecf20Sopenharmony_ci unsigned int clk_id) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct ccu_pll *pll; 858c2ecf20Sopenharmony_ci int idx; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci for (idx = 0; idx < CCU_PLL_NUM; ++idx) { 888c2ecf20Sopenharmony_ci pll = data->plls[idx]; 898c2ecf20Sopenharmony_ci if (pll && pll->id == clk_id) 908c2ecf20Sopenharmony_ci return pll; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic struct ccu_pll_data *ccu_pll_create_data(struct device_node *np) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci struct ccu_pll_data *data; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 1018c2ecf20Sopenharmony_ci if (!data) 1028c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci data->np = np; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return data; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic void ccu_pll_free_data(struct ccu_pll_data *data) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci kfree(data); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int ccu_pll_find_sys_regs(struct ccu_pll_data *data) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci data->sys_regs = syscon_node_to_regmap(data->np->parent); 1178c2ecf20Sopenharmony_ci if (IS_ERR(data->sys_regs)) { 1188c2ecf20Sopenharmony_ci pr_err("Failed to find syscon regs for '%s'\n", 1198c2ecf20Sopenharmony_ci of_node_full_name(data->np)); 1208c2ecf20Sopenharmony_ci return PTR_ERR(data->sys_regs); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec, 1278c2ecf20Sopenharmony_ci void *priv) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct ccu_pll_data *data = priv; 1308c2ecf20Sopenharmony_ci struct ccu_pll *pll; 1318c2ecf20Sopenharmony_ci unsigned int clk_id; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci clk_id = clkspec->args[0]; 1348c2ecf20Sopenharmony_ci pll = ccu_pll_find_desc(data, clk_id); 1358c2ecf20Sopenharmony_ci if (IS_ERR(pll)) { 1368c2ecf20Sopenharmony_ci pr_info("Invalid PLL clock ID %d specified\n", clk_id); 1378c2ecf20Sopenharmony_ci return ERR_CAST(pll); 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return ccu_pll_get_clk_hw(pll); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int ccu_pll_clk_register(struct ccu_pll_data *data) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci int idx, ret; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (idx = 0; idx < CCU_PLL_NUM; ++idx) { 1488c2ecf20Sopenharmony_ci const struct ccu_pll_info *info = &pll_info[idx]; 1498c2ecf20Sopenharmony_ci struct ccu_pll_init_data init = {0}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci init.id = info->id; 1528c2ecf20Sopenharmony_ci init.name = info->name; 1538c2ecf20Sopenharmony_ci init.parent_name = info->parent_name; 1548c2ecf20Sopenharmony_ci init.base = info->base; 1558c2ecf20Sopenharmony_ci init.sys_regs = data->sys_regs; 1568c2ecf20Sopenharmony_ci init.np = data->np; 1578c2ecf20Sopenharmony_ci init.flags = info->flags; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci data->plls[idx] = ccu_pll_hw_register(&init); 1608c2ecf20Sopenharmony_ci if (IS_ERR(data->plls[idx])) { 1618c2ecf20Sopenharmony_ci ret = PTR_ERR(data->plls[idx]); 1628c2ecf20Sopenharmony_ci pr_err("Couldn't register PLL hw '%s'\n", 1638c2ecf20Sopenharmony_ci init.name); 1648c2ecf20Sopenharmony_ci goto err_hw_unregister; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci ret = of_clk_add_hw_provider(data->np, ccu_pll_of_clk_hw_get, data); 1698c2ecf20Sopenharmony_ci if (ret) { 1708c2ecf20Sopenharmony_ci pr_err("Couldn't register PLL provider of '%s'\n", 1718c2ecf20Sopenharmony_ci of_node_full_name(data->np)); 1728c2ecf20Sopenharmony_ci goto err_hw_unregister; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cierr_hw_unregister: 1788c2ecf20Sopenharmony_ci for (--idx; idx >= 0; --idx) 1798c2ecf20Sopenharmony_ci ccu_pll_hw_unregister(data->plls[idx]); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic __init void ccu_pll_init(struct device_node *np) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct ccu_pll_data *data; 1878c2ecf20Sopenharmony_ci int ret; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci data = ccu_pll_create_data(np); 1908c2ecf20Sopenharmony_ci if (IS_ERR(data)) 1918c2ecf20Sopenharmony_ci return; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = ccu_pll_find_sys_regs(data); 1948c2ecf20Sopenharmony_ci if (ret) 1958c2ecf20Sopenharmony_ci goto err_free_data; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ret = ccu_pll_clk_register(data); 1988c2ecf20Sopenharmony_ci if (ret) 1998c2ecf20Sopenharmony_ci goto err_free_data; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci return; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cierr_free_data: 2048c2ecf20Sopenharmony_ci ccu_pll_free_data(data); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init); 207