clk-ccu-pll.c (4967B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 4 * 5 * Authors: 6 * Serge Semin <Sergey.Semin@baikalelectronics.ru> 7 * Dmitry Dunaev <dmitry.dunaev@baikalelectronics.ru> 8 * 9 * Baikal-T1 CCU PLL clocks driver 10 */ 11 12#define pr_fmt(fmt) "bt1-ccu-pll: " fmt 13 14#include <linux/kernel.h> 15#include <linux/printk.h> 16#include <linux/slab.h> 17#include <linux/clk-provider.h> 18#include <linux/mfd/syscon.h> 19#include <linux/of.h> 20#include <linux/of_address.h> 21#include <linux/ioport.h> 22#include <linux/regmap.h> 23 24#include <dt-bindings/clock/bt1-ccu.h> 25 26#include "ccu-pll.h" 27 28#define CCU_CPU_PLL_BASE 0x000 29#define CCU_SATA_PLL_BASE 0x008 30#define CCU_DDR_PLL_BASE 0x010 31#define CCU_PCIE_PLL_BASE 0x018 32#define CCU_ETH_PLL_BASE 0x020 33 34#define CCU_PLL_INFO(_id, _name, _pname, _base, _flags) \ 35 { \ 36 .id = _id, \ 37 .name = _name, \ 38 .parent_name = _pname, \ 39 .base = _base, \ 40 .flags = _flags \ 41 } 42 43#define CCU_PLL_NUM ARRAY_SIZE(pll_info) 44 45struct ccu_pll_info { 46 unsigned int id; 47 const char *name; 48 const char *parent_name; 49 unsigned int base; 50 unsigned long flags; 51}; 52 53/* 54 * Alas we have to mark all PLLs as critical. CPU and DDR PLLs are sources of 55 * CPU cores and DDR controller reference clocks, due to which they obviously 56 * shouldn't be ever gated. SATA and PCIe PLLs are the parents of APB-bus and 57 * DDR controller AXI-bus clocks. If they are gated the system will be 58 * unusable. Moreover disabling SATA and Ethernet PLLs causes automatic reset 59 * of the corresponding subsystems. So until we aren't ready to re-initialize 60 * all the devices consuming those PLLs, they will be marked as critical too. 61 */ 62static const struct ccu_pll_info pll_info[] = { 63 CCU_PLL_INFO(CCU_CPU_PLL, "cpu_pll", "ref_clk", CCU_CPU_PLL_BASE, 64 CLK_IS_CRITICAL), 65 CCU_PLL_INFO(CCU_SATA_PLL, "sata_pll", "ref_clk", CCU_SATA_PLL_BASE, 66 CLK_IS_CRITICAL | CLK_SET_RATE_GATE), 67 CCU_PLL_INFO(CCU_DDR_PLL, "ddr_pll", "ref_clk", CCU_DDR_PLL_BASE, 68 CLK_IS_CRITICAL | CLK_SET_RATE_GATE), 69 CCU_PLL_INFO(CCU_PCIE_PLL, "pcie_pll", "ref_clk", CCU_PCIE_PLL_BASE, 70 CLK_IS_CRITICAL), 71 CCU_PLL_INFO(CCU_ETH_PLL, "eth_pll", "ref_clk", CCU_ETH_PLL_BASE, 72 CLK_IS_CRITICAL | CLK_SET_RATE_GATE) 73}; 74 75struct ccu_pll_data { 76 struct device_node *np; 77 struct regmap *sys_regs; 78 struct ccu_pll *plls[CCU_PLL_NUM]; 79}; 80 81static struct ccu_pll *ccu_pll_find_desc(struct ccu_pll_data *data, 82 unsigned int clk_id) 83{ 84 struct ccu_pll *pll; 85 int idx; 86 87 for (idx = 0; idx < CCU_PLL_NUM; ++idx) { 88 pll = data->plls[idx]; 89 if (pll && pll->id == clk_id) 90 return pll; 91 } 92 93 return ERR_PTR(-EINVAL); 94} 95 96static struct ccu_pll_data *ccu_pll_create_data(struct device_node *np) 97{ 98 struct ccu_pll_data *data; 99 100 data = kzalloc(sizeof(*data), GFP_KERNEL); 101 if (!data) 102 return ERR_PTR(-ENOMEM); 103 104 data->np = np; 105 106 return data; 107} 108 109static void ccu_pll_free_data(struct ccu_pll_data *data) 110{ 111 kfree(data); 112} 113 114static int ccu_pll_find_sys_regs(struct ccu_pll_data *data) 115{ 116 data->sys_regs = syscon_node_to_regmap(data->np->parent); 117 if (IS_ERR(data->sys_regs)) { 118 pr_err("Failed to find syscon regs for '%s'\n", 119 of_node_full_name(data->np)); 120 return PTR_ERR(data->sys_regs); 121 } 122 123 return 0; 124} 125 126static struct clk_hw *ccu_pll_of_clk_hw_get(struct of_phandle_args *clkspec, 127 void *priv) 128{ 129 struct ccu_pll_data *data = priv; 130 struct ccu_pll *pll; 131 unsigned int clk_id; 132 133 clk_id = clkspec->args[0]; 134 pll = ccu_pll_find_desc(data, clk_id); 135 if (IS_ERR(pll)) { 136 pr_info("Invalid PLL clock ID %d specified\n", clk_id); 137 return ERR_CAST(pll); 138 } 139 140 return ccu_pll_get_clk_hw(pll); 141} 142 143static int ccu_pll_clk_register(struct ccu_pll_data *data) 144{ 145 int idx, ret; 146 147 for (idx = 0; idx < CCU_PLL_NUM; ++idx) { 148 const struct ccu_pll_info *info = &pll_info[idx]; 149 struct ccu_pll_init_data init = {0}; 150 151 init.id = info->id; 152 init.name = info->name; 153 init.parent_name = info->parent_name; 154 init.base = info->base; 155 init.sys_regs = data->sys_regs; 156 init.np = data->np; 157 init.flags = info->flags; 158 159 data->plls[idx] = ccu_pll_hw_register(&init); 160 if (IS_ERR(data->plls[idx])) { 161 ret = PTR_ERR(data->plls[idx]); 162 pr_err("Couldn't register PLL hw '%s'\n", 163 init.name); 164 goto err_hw_unregister; 165 } 166 } 167 168 ret = of_clk_add_hw_provider(data->np, ccu_pll_of_clk_hw_get, data); 169 if (ret) { 170 pr_err("Couldn't register PLL provider of '%s'\n", 171 of_node_full_name(data->np)); 172 goto err_hw_unregister; 173 } 174 175 return 0; 176 177err_hw_unregister: 178 for (--idx; idx >= 0; --idx) 179 ccu_pll_hw_unregister(data->plls[idx]); 180 181 return ret; 182} 183 184static __init void ccu_pll_init(struct device_node *np) 185{ 186 struct ccu_pll_data *data; 187 int ret; 188 189 data = ccu_pll_create_data(np); 190 if (IS_ERR(data)) 191 return; 192 193 ret = ccu_pll_find_sys_regs(data); 194 if (ret) 195 goto err_free_data; 196 197 ret = ccu_pll_clk_register(data); 198 if (ret) 199 goto err_free_data; 200 201 return; 202 203err_free_data: 204 ccu_pll_free_data(data); 205} 206CLK_OF_DECLARE(ccu_pll, "baikal,bt1-ccu-pll", ccu_pll_init);