1c816e1ddSZong Li // SPDX-License-Identifier: GPL-2.0 2c816e1ddSZong Li /* 3c816e1ddSZong Li * Copyright (C) 2020 SiFive, Inc. 4c816e1ddSZong Li * Copyright (C) 2020 Zong Li 5c816e1ddSZong Li */ 6c816e1ddSZong Li 7c816e1ddSZong Li #include <linux/clkdev.h> 8c816e1ddSZong Li #include <linux/delay.h> 9c816e1ddSZong Li #include <linux/io.h> 10c816e1ddSZong Li #include <linux/of_device.h> 11c816e1ddSZong Li #include "sifive-prci.h" 12c816e1ddSZong Li #include "fu540-prci.h" 13efc91ae4SZong Li #include "fu740-prci.h" 14c816e1ddSZong Li 15487dc7bbSLee Jones static const struct prci_clk_desc prci_clk_fu540 = { 16487dc7bbSLee Jones .clks = __prci_init_clocks_fu540, 17487dc7bbSLee Jones .num_clks = ARRAY_SIZE(__prci_init_clocks_fu540), 18487dc7bbSLee Jones }; 19487dc7bbSLee Jones 20c816e1ddSZong Li /* 21c816e1ddSZong Li * Private functions 22c816e1ddSZong Li */ 23c816e1ddSZong Li 24c816e1ddSZong Li /** 25c816e1ddSZong Li * __prci_readl() - read from a PRCI register 26c816e1ddSZong Li * @pd: PRCI context 27c816e1ddSZong Li * @offs: register offset to read from (in bytes, from PRCI base address) 28c816e1ddSZong Li * 29c816e1ddSZong Li * Read the register located at offset @offs from the base virtual 30c816e1ddSZong Li * address of the PRCI register target described by @pd, and return 31c816e1ddSZong Li * the value to the caller. 32c816e1ddSZong Li * 33c816e1ddSZong Li * Context: Any context. 34c816e1ddSZong Li * 35c816e1ddSZong Li * Return: the contents of the register described by @pd and @offs. 36c816e1ddSZong Li */ 37c816e1ddSZong Li static u32 __prci_readl(struct __prci_data *pd, u32 offs) 38c816e1ddSZong Li { 39c816e1ddSZong Li return readl_relaxed(pd->va + offs); 40c816e1ddSZong Li } 41c816e1ddSZong Li 42c816e1ddSZong Li static void __prci_writel(u32 v, u32 offs, struct __prci_data *pd) 43c816e1ddSZong Li { 44c816e1ddSZong Li writel_relaxed(v, pd->va + offs); 45c816e1ddSZong Li } 46c816e1ddSZong Li 47c816e1ddSZong Li /* WRPLL-related private functions */ 48c816e1ddSZong Li 49c816e1ddSZong Li /** 50c816e1ddSZong Li * __prci_wrpll_unpack() - unpack WRPLL configuration registers into parameters 51c816e1ddSZong Li * @c: ptr to a struct wrpll_cfg record to write config into 52c816e1ddSZong Li * @r: value read from the PRCI PLL configuration register 53c816e1ddSZong Li * 54c816e1ddSZong Li * Given a value @r read from an FU740 PRCI PLL configuration register, 55c816e1ddSZong Li * split it into fields and populate it into the WRPLL configuration record 56c816e1ddSZong Li * pointed to by @c. 57c816e1ddSZong Li * 58c816e1ddSZong Li * The COREPLLCFG0 macros are used below, but the other *PLLCFG0 macros 59c816e1ddSZong Li * have the same register layout. 60c816e1ddSZong Li * 61c816e1ddSZong Li * Context: Any context. 62c816e1ddSZong Li */ 63c816e1ddSZong Li static void __prci_wrpll_unpack(struct wrpll_cfg *c, u32 r) 64c816e1ddSZong Li { 65c816e1ddSZong Li u32 v; 66c816e1ddSZong Li 67c816e1ddSZong Li v = r & PRCI_COREPLLCFG0_DIVR_MASK; 68c816e1ddSZong Li v >>= PRCI_COREPLLCFG0_DIVR_SHIFT; 69c816e1ddSZong Li c->divr = v; 70c816e1ddSZong Li 71c816e1ddSZong Li v = r & PRCI_COREPLLCFG0_DIVF_MASK; 72c816e1ddSZong Li v >>= PRCI_COREPLLCFG0_DIVF_SHIFT; 73c816e1ddSZong Li c->divf = v; 74c816e1ddSZong Li 75c816e1ddSZong Li v = r & PRCI_COREPLLCFG0_DIVQ_MASK; 76c816e1ddSZong Li v >>= PRCI_COREPLLCFG0_DIVQ_SHIFT; 77c816e1ddSZong Li c->divq = v; 78c816e1ddSZong Li 79c816e1ddSZong Li v = r & PRCI_COREPLLCFG0_RANGE_MASK; 80c816e1ddSZong Li v >>= PRCI_COREPLLCFG0_RANGE_SHIFT; 81c816e1ddSZong Li c->range = v; 82c816e1ddSZong Li 83c816e1ddSZong Li c->flags &= 84c816e1ddSZong Li (WRPLL_FLAGS_INT_FEEDBACK_MASK | WRPLL_FLAGS_EXT_FEEDBACK_MASK); 85c816e1ddSZong Li 86c816e1ddSZong Li /* external feedback mode not supported */ 87c816e1ddSZong Li c->flags |= WRPLL_FLAGS_INT_FEEDBACK_MASK; 88c816e1ddSZong Li } 89c816e1ddSZong Li 90c816e1ddSZong Li /** 91c816e1ddSZong Li * __prci_wrpll_pack() - pack PLL configuration parameters into a register value 92c816e1ddSZong Li * @c: pointer to a struct wrpll_cfg record containing the PLL's cfg 93c816e1ddSZong Li * 94c816e1ddSZong Li * Using a set of WRPLL configuration values pointed to by @c, 95c816e1ddSZong Li * assemble a PRCI PLL configuration register value, and return it to 96c816e1ddSZong Li * the caller. 97c816e1ddSZong Li * 98c816e1ddSZong Li * Context: Any context. Caller must ensure that the contents of the 99c816e1ddSZong Li * record pointed to by @c do not change during the execution 100c816e1ddSZong Li * of this function. 101c816e1ddSZong Li * 102c816e1ddSZong Li * Returns: a value suitable for writing into a PRCI PLL configuration 103c816e1ddSZong Li * register 104c816e1ddSZong Li */ 105c816e1ddSZong Li static u32 __prci_wrpll_pack(const struct wrpll_cfg *c) 106c816e1ddSZong Li { 107c816e1ddSZong Li u32 r = 0; 108c816e1ddSZong Li 109c816e1ddSZong Li r |= c->divr << PRCI_COREPLLCFG0_DIVR_SHIFT; 110c816e1ddSZong Li r |= c->divf << PRCI_COREPLLCFG0_DIVF_SHIFT; 111c816e1ddSZong Li r |= c->divq << PRCI_COREPLLCFG0_DIVQ_SHIFT; 112c816e1ddSZong Li r |= c->range << PRCI_COREPLLCFG0_RANGE_SHIFT; 113c816e1ddSZong Li 114c816e1ddSZong Li /* external feedback mode not supported */ 115c816e1ddSZong Li r |= PRCI_COREPLLCFG0_FSE_MASK; 116c816e1ddSZong Li 117c816e1ddSZong Li return r; 118c816e1ddSZong Li } 119c816e1ddSZong Li 120c816e1ddSZong Li /** 121732374a0SPragnesh Patel * __prci_wrpll_read_cfg0() - read the WRPLL configuration from the PRCI 122c816e1ddSZong Li * @pd: PRCI context 123c816e1ddSZong Li * @pwd: PRCI WRPLL metadata 124c816e1ddSZong Li * 125c816e1ddSZong Li * Read the current configuration of the PLL identified by @pwd from 126c816e1ddSZong Li * the PRCI identified by @pd, and store it into the local configuration 127c816e1ddSZong Li * cache in @pwd. 128c816e1ddSZong Li * 129c816e1ddSZong Li * Context: Any context. Caller must prevent the records pointed to by 130c816e1ddSZong Li * @pd and @pwd from changing during execution. 131c816e1ddSZong Li */ 132732374a0SPragnesh Patel static void __prci_wrpll_read_cfg0(struct __prci_data *pd, 133c816e1ddSZong Li struct __prci_wrpll_data *pwd) 134c816e1ddSZong Li { 135c816e1ddSZong Li __prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs)); 136c816e1ddSZong Li } 137c816e1ddSZong Li 138c816e1ddSZong Li /** 139732374a0SPragnesh Patel * __prci_wrpll_write_cfg0() - write WRPLL configuration into the PRCI 140c816e1ddSZong Li * @pd: PRCI context 141c816e1ddSZong Li * @pwd: PRCI WRPLL metadata 142c816e1ddSZong Li * @c: WRPLL configuration record to write 143c816e1ddSZong Li * 144c816e1ddSZong Li * Write the WRPLL configuration described by @c into the WRPLL 145c816e1ddSZong Li * configuration register identified by @pwd in the PRCI instance 146c816e1ddSZong Li * described by @c. Make a cached copy of the WRPLL's current 147c816e1ddSZong Li * configuration so it can be used by other code. 148c816e1ddSZong Li * 149c816e1ddSZong Li * Context: Any context. Caller must prevent the records pointed to by 150c816e1ddSZong Li * @pd and @pwd from changing during execution. 151c816e1ddSZong Li */ 152732374a0SPragnesh Patel static void __prci_wrpll_write_cfg0(struct __prci_data *pd, 153c816e1ddSZong Li struct __prci_wrpll_data *pwd, 154c816e1ddSZong Li struct wrpll_cfg *c) 155c816e1ddSZong Li { 156c816e1ddSZong Li __prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd); 157c816e1ddSZong Li 158c816e1ddSZong Li memcpy(&pwd->c, c, sizeof(*c)); 159c816e1ddSZong Li } 160c816e1ddSZong Li 161732374a0SPragnesh Patel /** 162732374a0SPragnesh Patel * __prci_wrpll_write_cfg1() - write Clock enable/disable configuration 163732374a0SPragnesh Patel * into the PRCI 164732374a0SPragnesh Patel * @pd: PRCI context 165732374a0SPragnesh Patel * @pwd: PRCI WRPLL metadata 166732374a0SPragnesh Patel * @enable: Clock enable or disable value 167732374a0SPragnesh Patel */ 168732374a0SPragnesh Patel static void __prci_wrpll_write_cfg1(struct __prci_data *pd, 169732374a0SPragnesh Patel struct __prci_wrpll_data *pwd, 170732374a0SPragnesh Patel u32 enable) 171732374a0SPragnesh Patel { 172732374a0SPragnesh Patel __prci_writel(enable, pwd->cfg1_offs, pd); 173732374a0SPragnesh Patel } 174732374a0SPragnesh Patel 175c816e1ddSZong Li /* 176c816e1ddSZong Li * Linux clock framework integration 177c816e1ddSZong Li * 178c816e1ddSZong Li * See the Linux clock framework documentation for more information on 179c816e1ddSZong Li * these functions. 180c816e1ddSZong Li */ 181c816e1ddSZong Li 182c816e1ddSZong Li unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw *hw, 183c816e1ddSZong Li unsigned long parent_rate) 184c816e1ddSZong Li { 185c816e1ddSZong Li struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 186c816e1ddSZong Li struct __prci_wrpll_data *pwd = pc->pwd; 187c816e1ddSZong Li 188c816e1ddSZong Li return wrpll_calc_output_rate(&pwd->c, parent_rate); 189c816e1ddSZong Li } 190c816e1ddSZong Li 191c816e1ddSZong Li long sifive_prci_wrpll_round_rate(struct clk_hw *hw, 192c816e1ddSZong Li unsigned long rate, 193c816e1ddSZong Li unsigned long *parent_rate) 194c816e1ddSZong Li { 195c816e1ddSZong Li struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 196c816e1ddSZong Li struct __prci_wrpll_data *pwd = pc->pwd; 197c816e1ddSZong Li struct wrpll_cfg c; 198c816e1ddSZong Li 199c816e1ddSZong Li memcpy(&c, &pwd->c, sizeof(c)); 200c816e1ddSZong Li 201c816e1ddSZong Li wrpll_configure_for_rate(&c, rate, *parent_rate); 202c816e1ddSZong Li 203c816e1ddSZong Li return wrpll_calc_output_rate(&c, *parent_rate); 204c816e1ddSZong Li } 205c816e1ddSZong Li 206c816e1ddSZong Li int sifive_prci_wrpll_set_rate(struct clk_hw *hw, 207c816e1ddSZong Li unsigned long rate, unsigned long parent_rate) 208c816e1ddSZong Li { 209c816e1ddSZong Li struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 210c816e1ddSZong Li struct __prci_wrpll_data *pwd = pc->pwd; 211c816e1ddSZong Li struct __prci_data *pd = pc->pd; 212c816e1ddSZong Li int r; 213c816e1ddSZong Li 214c816e1ddSZong Li r = wrpll_configure_for_rate(&pwd->c, rate, parent_rate); 215c816e1ddSZong Li if (r) 216c816e1ddSZong Li return r; 217c816e1ddSZong Li 218c816e1ddSZong Li if (pwd->enable_bypass) 219c816e1ddSZong Li pwd->enable_bypass(pd); 220c816e1ddSZong Li 221732374a0SPragnesh Patel __prci_wrpll_write_cfg0(pd, pwd, &pwd->c); 222c816e1ddSZong Li 223c816e1ddSZong Li udelay(wrpll_calc_max_lock_us(&pwd->c)); 224c816e1ddSZong Li 225732374a0SPragnesh Patel return 0; 226732374a0SPragnesh Patel } 227732374a0SPragnesh Patel 228732374a0SPragnesh Patel int sifive_clk_is_enabled(struct clk_hw *hw) 229732374a0SPragnesh Patel { 230732374a0SPragnesh Patel struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 231732374a0SPragnesh Patel struct __prci_wrpll_data *pwd = pc->pwd; 232732374a0SPragnesh Patel struct __prci_data *pd = pc->pd; 233732374a0SPragnesh Patel u32 r; 234732374a0SPragnesh Patel 235732374a0SPragnesh Patel r = __prci_readl(pd, pwd->cfg1_offs); 236732374a0SPragnesh Patel 237732374a0SPragnesh Patel if (r & PRCI_COREPLLCFG1_CKE_MASK) 238732374a0SPragnesh Patel return 1; 239732374a0SPragnesh Patel else 240732374a0SPragnesh Patel return 0; 241732374a0SPragnesh Patel } 242732374a0SPragnesh Patel 243732374a0SPragnesh Patel int sifive_prci_clock_enable(struct clk_hw *hw) 244732374a0SPragnesh Patel { 245732374a0SPragnesh Patel struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 246732374a0SPragnesh Patel struct __prci_wrpll_data *pwd = pc->pwd; 247732374a0SPragnesh Patel struct __prci_data *pd = pc->pd; 248732374a0SPragnesh Patel 249732374a0SPragnesh Patel if (sifive_clk_is_enabled(hw)) 250732374a0SPragnesh Patel return 0; 251732374a0SPragnesh Patel 252732374a0SPragnesh Patel __prci_wrpll_write_cfg1(pd, pwd, PRCI_COREPLLCFG1_CKE_MASK); 253732374a0SPragnesh Patel 254c816e1ddSZong Li if (pwd->disable_bypass) 255c816e1ddSZong Li pwd->disable_bypass(pd); 256c816e1ddSZong Li 257c816e1ddSZong Li return 0; 258c816e1ddSZong Li } 259c816e1ddSZong Li 260732374a0SPragnesh Patel void sifive_prci_clock_disable(struct clk_hw *hw) 261732374a0SPragnesh Patel { 262732374a0SPragnesh Patel struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 263732374a0SPragnesh Patel struct __prci_wrpll_data *pwd = pc->pwd; 264732374a0SPragnesh Patel struct __prci_data *pd = pc->pd; 265732374a0SPragnesh Patel u32 r; 266732374a0SPragnesh Patel 267732374a0SPragnesh Patel if (pwd->enable_bypass) 268732374a0SPragnesh Patel pwd->enable_bypass(pd); 269732374a0SPragnesh Patel 270732374a0SPragnesh Patel r = __prci_readl(pd, pwd->cfg1_offs); 271732374a0SPragnesh Patel r &= ~PRCI_COREPLLCFG1_CKE_MASK; 272732374a0SPragnesh Patel 273732374a0SPragnesh Patel __prci_wrpll_write_cfg1(pd, pwd, r); 274732374a0SPragnesh Patel } 275732374a0SPragnesh Patel 276c816e1ddSZong Li /* TLCLKSEL clock integration */ 277c816e1ddSZong Li 278c816e1ddSZong Li unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw, 279c816e1ddSZong Li unsigned long parent_rate) 280c816e1ddSZong Li { 281c816e1ddSZong Li struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 282c816e1ddSZong Li struct __prci_data *pd = pc->pd; 283c816e1ddSZong Li u32 v; 284c816e1ddSZong Li u8 div; 285c816e1ddSZong Li 286c816e1ddSZong Li v = __prci_readl(pd, PRCI_CLKMUXSTATUSREG_OFFSET); 287c816e1ddSZong Li v &= PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK; 288c816e1ddSZong Li div = v ? 1 : 2; 289c816e1ddSZong Li 290c816e1ddSZong Li return div_u64(parent_rate, div); 291c816e1ddSZong Li } 292c816e1ddSZong Li 293efc91ae4SZong Li /* HFPCLK clock integration */ 294efc91ae4SZong Li 295efc91ae4SZong Li unsigned long sifive_prci_hfpclkplldiv_recalc_rate(struct clk_hw *hw, 296efc91ae4SZong Li unsigned long parent_rate) 297efc91ae4SZong Li { 298efc91ae4SZong Li struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 299efc91ae4SZong Li struct __prci_data *pd = pc->pd; 300efc91ae4SZong Li u32 div = __prci_readl(pd, PRCI_HFPCLKPLLDIV_OFFSET); 301efc91ae4SZong Li 302efc91ae4SZong Li return div_u64(parent_rate, div + 2); 303efc91ae4SZong Li } 304efc91ae4SZong Li 305c816e1ddSZong Li /* 306c816e1ddSZong Li * Core clock mux control 307c816e1ddSZong Li */ 308c816e1ddSZong Li 309c816e1ddSZong Li /** 310c816e1ddSZong Li * sifive_prci_coreclksel_use_hfclk() - switch the CORECLK mux to output HFCLK 311c816e1ddSZong Li * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 312c816e1ddSZong Li * 313c816e1ddSZong Li * Switch the CORECLK mux to the HFCLK input source; return once complete. 314c816e1ddSZong Li * 315c816e1ddSZong Li * Context: Any context. Caller must prevent concurrent changes to the 316c816e1ddSZong Li * PRCI_CORECLKSEL_OFFSET register. 317c816e1ddSZong Li */ 318c816e1ddSZong Li void sifive_prci_coreclksel_use_hfclk(struct __prci_data *pd) 319c816e1ddSZong Li { 320c816e1ddSZong Li u32 r; 321c816e1ddSZong Li 322c816e1ddSZong Li r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 323c816e1ddSZong Li r |= PRCI_CORECLKSEL_CORECLKSEL_MASK; 324c816e1ddSZong Li __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 325c816e1ddSZong Li 326c816e1ddSZong Li r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 327c816e1ddSZong Li } 328c816e1ddSZong Li 329c816e1ddSZong Li /** 330c816e1ddSZong Li * sifive_prci_coreclksel_use_corepll() - switch the CORECLK mux to output 331c816e1ddSZong Li * COREPLL 332c816e1ddSZong Li * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 333c816e1ddSZong Li * 334c816e1ddSZong Li * Switch the CORECLK mux to the COREPLL output clock; return once complete. 335c816e1ddSZong Li * 336c816e1ddSZong Li * Context: Any context. Caller must prevent concurrent changes to the 337c816e1ddSZong Li * PRCI_CORECLKSEL_OFFSET register. 338c816e1ddSZong Li */ 339c816e1ddSZong Li void sifive_prci_coreclksel_use_corepll(struct __prci_data *pd) 340c816e1ddSZong Li { 341c816e1ddSZong Li u32 r; 342c816e1ddSZong Li 343c816e1ddSZong Li r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 344c816e1ddSZong Li r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK; 345c816e1ddSZong Li __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 346c816e1ddSZong Li 347c816e1ddSZong Li r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 348c816e1ddSZong Li } 349c816e1ddSZong Li 350c816e1ddSZong Li /** 351efc91ae4SZong Li * sifive_prci_coreclksel_use_final_corepll() - switch the CORECLK mux to output 352efc91ae4SZong Li * FINAL_COREPLL 353efc91ae4SZong Li * @pd: struct __prci_data * for the PRCI containing the CORECLK mux reg 354efc91ae4SZong Li * 355efc91ae4SZong Li * Switch the CORECLK mux to the final COREPLL output clock; return once 356efc91ae4SZong Li * complete. 357efc91ae4SZong Li * 358efc91ae4SZong Li * Context: Any context. Caller must prevent concurrent changes to the 359efc91ae4SZong Li * PRCI_CORECLKSEL_OFFSET register. 360efc91ae4SZong Li */ 361efc91ae4SZong Li void sifive_prci_coreclksel_use_final_corepll(struct __prci_data *pd) 362efc91ae4SZong Li { 363efc91ae4SZong Li u32 r; 364efc91ae4SZong Li 365efc91ae4SZong Li r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); 366efc91ae4SZong Li r &= ~PRCI_CORECLKSEL_CORECLKSEL_MASK; 367efc91ae4SZong Li __prci_writel(r, PRCI_CORECLKSEL_OFFSET, pd); 368efc91ae4SZong Li 369efc91ae4SZong Li r = __prci_readl(pd, PRCI_CORECLKSEL_OFFSET); /* barrier */ 370efc91ae4SZong Li } 371efc91ae4SZong Li 372efc91ae4SZong Li /** 373efc91ae4SZong Li * sifive_prci_corepllsel_use_dvfscorepll() - switch the COREPLL mux to 374efc91ae4SZong Li * output DVFS_COREPLL 375efc91ae4SZong Li * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg 376efc91ae4SZong Li * 377efc91ae4SZong Li * Switch the COREPLL mux to the DVFSCOREPLL output clock; return once complete. 378efc91ae4SZong Li * 379efc91ae4SZong Li * Context: Any context. Caller must prevent concurrent changes to the 380efc91ae4SZong Li * PRCI_COREPLLSEL_OFFSET register. 381efc91ae4SZong Li */ 382efc91ae4SZong Li void sifive_prci_corepllsel_use_dvfscorepll(struct __prci_data *pd) 383efc91ae4SZong Li { 384efc91ae4SZong Li u32 r; 385efc91ae4SZong Li 386efc91ae4SZong Li r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); 387efc91ae4SZong Li r |= PRCI_COREPLLSEL_COREPLLSEL_MASK; 388efc91ae4SZong Li __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd); 389efc91ae4SZong Li 390efc91ae4SZong Li r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); /* barrier */ 391efc91ae4SZong Li } 392efc91ae4SZong Li 393efc91ae4SZong Li /** 394efc91ae4SZong Li * sifive_prci_corepllsel_use_corepll() - switch the COREPLL mux to 395efc91ae4SZong Li * output COREPLL 396efc91ae4SZong Li * @pd: struct __prci_data * for the PRCI containing the COREPLL mux reg 397efc91ae4SZong Li * 398efc91ae4SZong Li * Switch the COREPLL mux to the COREPLL output clock; return once complete. 399efc91ae4SZong Li * 400efc91ae4SZong Li * Context: Any context. Caller must prevent concurrent changes to the 401efc91ae4SZong Li * PRCI_COREPLLSEL_OFFSET register. 402efc91ae4SZong Li */ 403efc91ae4SZong Li void sifive_prci_corepllsel_use_corepll(struct __prci_data *pd) 404efc91ae4SZong Li { 405efc91ae4SZong Li u32 r; 406efc91ae4SZong Li 407efc91ae4SZong Li r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); 408efc91ae4SZong Li r &= ~PRCI_COREPLLSEL_COREPLLSEL_MASK; 409efc91ae4SZong Li __prci_writel(r, PRCI_COREPLLSEL_OFFSET, pd); 410efc91ae4SZong Li 411efc91ae4SZong Li r = __prci_readl(pd, PRCI_COREPLLSEL_OFFSET); /* barrier */ 412efc91ae4SZong Li } 413efc91ae4SZong Li 414efc91ae4SZong Li /** 415efc91ae4SZong Li * sifive_prci_hfpclkpllsel_use_hfclk() - switch the HFPCLKPLL mux to 416efc91ae4SZong Li * output HFCLK 417efc91ae4SZong Li * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg 418efc91ae4SZong Li * 419efc91ae4SZong Li * Switch the HFPCLKPLL mux to the HFCLK input source; return once complete. 420efc91ae4SZong Li * 421efc91ae4SZong Li * Context: Any context. Caller must prevent concurrent changes to the 422efc91ae4SZong Li * PRCI_HFPCLKPLLSEL_OFFSET register. 423efc91ae4SZong Li */ 424efc91ae4SZong Li void sifive_prci_hfpclkpllsel_use_hfclk(struct __prci_data *pd) 425efc91ae4SZong Li { 426efc91ae4SZong Li u32 r; 427efc91ae4SZong Li 428efc91ae4SZong Li r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); 429efc91ae4SZong Li r |= PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK; 430efc91ae4SZong Li __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd); 431efc91ae4SZong Li 432efc91ae4SZong Li r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */ 433efc91ae4SZong Li } 434efc91ae4SZong Li 435efc91ae4SZong Li /** 436efc91ae4SZong Li * sifive_prci_hfpclkpllsel_use_hfpclkpll() - switch the HFPCLKPLL mux to 437efc91ae4SZong Li * output HFPCLKPLL 438efc91ae4SZong Li * @pd: struct __prci_data * for the PRCI containing the HFPCLKPLL mux reg 439efc91ae4SZong Li * 440efc91ae4SZong Li * Switch the HFPCLKPLL mux to the HFPCLKPLL output clock; return once complete. 441efc91ae4SZong Li * 442efc91ae4SZong Li * Context: Any context. Caller must prevent concurrent changes to the 443efc91ae4SZong Li * PRCI_HFPCLKPLLSEL_OFFSET register. 444efc91ae4SZong Li */ 445efc91ae4SZong Li void sifive_prci_hfpclkpllsel_use_hfpclkpll(struct __prci_data *pd) 446efc91ae4SZong Li { 447efc91ae4SZong Li u32 r; 448efc91ae4SZong Li 449efc91ae4SZong Li r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); 450efc91ae4SZong Li r &= ~PRCI_HFPCLKPLLSEL_HFPCLKPLLSEL_MASK; 451efc91ae4SZong Li __prci_writel(r, PRCI_HFPCLKPLLSEL_OFFSET, pd); 452efc91ae4SZong Li 453efc91ae4SZong Li r = __prci_readl(pd, PRCI_HFPCLKPLLSEL_OFFSET); /* barrier */ 454efc91ae4SZong Li } 455efc91ae4SZong Li 456*c61287bfSGreentime Hu /* PCIE AUX clock APIs for enable, disable. */ 457*c61287bfSGreentime Hu int sifive_prci_pcie_aux_clock_is_enabled(struct clk_hw *hw) 458*c61287bfSGreentime Hu { 459*c61287bfSGreentime Hu struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 460*c61287bfSGreentime Hu struct __prci_data *pd = pc->pd; 461*c61287bfSGreentime Hu u32 r; 462*c61287bfSGreentime Hu 463*c61287bfSGreentime Hu r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); 464*c61287bfSGreentime Hu 465*c61287bfSGreentime Hu if (r & PRCI_PCIE_AUX_EN_MASK) 466*c61287bfSGreentime Hu return 1; 467*c61287bfSGreentime Hu else 468*c61287bfSGreentime Hu return 0; 469*c61287bfSGreentime Hu } 470*c61287bfSGreentime Hu 471*c61287bfSGreentime Hu int sifive_prci_pcie_aux_clock_enable(struct clk_hw *hw) 472*c61287bfSGreentime Hu { 473*c61287bfSGreentime Hu struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 474*c61287bfSGreentime Hu struct __prci_data *pd = pc->pd; 475*c61287bfSGreentime Hu u32 r __maybe_unused; 476*c61287bfSGreentime Hu 477*c61287bfSGreentime Hu if (sifive_prci_pcie_aux_clock_is_enabled(hw)) 478*c61287bfSGreentime Hu return 0; 479*c61287bfSGreentime Hu 480*c61287bfSGreentime Hu __prci_writel(1, PRCI_PCIE_AUX_OFFSET, pd); 481*c61287bfSGreentime Hu r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); /* barrier */ 482*c61287bfSGreentime Hu 483*c61287bfSGreentime Hu return 0; 484*c61287bfSGreentime Hu } 485*c61287bfSGreentime Hu 486*c61287bfSGreentime Hu void sifive_prci_pcie_aux_clock_disable(struct clk_hw *hw) 487*c61287bfSGreentime Hu { 488*c61287bfSGreentime Hu struct __prci_clock *pc = clk_hw_to_prci_clock(hw); 489*c61287bfSGreentime Hu struct __prci_data *pd = pc->pd; 490*c61287bfSGreentime Hu u32 r __maybe_unused; 491*c61287bfSGreentime Hu 492*c61287bfSGreentime Hu __prci_writel(0, PRCI_PCIE_AUX_OFFSET, pd); 493*c61287bfSGreentime Hu r = __prci_readl(pd, PRCI_PCIE_AUX_OFFSET); /* barrier */ 494*c61287bfSGreentime Hu 495*c61287bfSGreentime Hu } 496*c61287bfSGreentime Hu 497efc91ae4SZong Li /** 498c816e1ddSZong Li * __prci_register_clocks() - register clock controls in the PRCI 499c816e1ddSZong Li * @dev: Linux struct device 500c816e1ddSZong Li * @pd: The pointer for PRCI per-device instance data 501c816e1ddSZong Li * @desc: The pointer for the information of clocks of each SoCs 502c816e1ddSZong Li * 503c816e1ddSZong Li * Register the list of clock controls described in __prci_init_clocks[] with 504c816e1ddSZong Li * the Linux clock framework. 505c816e1ddSZong Li * 506c816e1ddSZong Li * Return: 0 upon success or a negative error code upon failure. 507c816e1ddSZong Li */ 508c816e1ddSZong Li static int __prci_register_clocks(struct device *dev, struct __prci_data *pd, 509c816e1ddSZong Li const struct prci_clk_desc *desc) 510c816e1ddSZong Li { 511c816e1ddSZong Li struct clk_init_data init = { }; 512c816e1ddSZong Li struct __prci_clock *pic; 513c816e1ddSZong Li int parent_count, i, r; 514c816e1ddSZong Li 515c816e1ddSZong Li parent_count = of_clk_get_parent_count(dev->of_node); 516c816e1ddSZong Li if (parent_count != EXPECTED_CLK_PARENT_COUNT) { 517c816e1ddSZong Li dev_err(dev, "expected only two parent clocks, found %d\n", 518c816e1ddSZong Li parent_count); 519c816e1ddSZong Li return -EINVAL; 520c816e1ddSZong Li } 521c816e1ddSZong Li 522c816e1ddSZong Li /* Register PLLs */ 523c816e1ddSZong Li for (i = 0; i < desc->num_clks; ++i) { 524c816e1ddSZong Li pic = &(desc->clks[i]); 525c816e1ddSZong Li 526c816e1ddSZong Li init.name = pic->name; 527c816e1ddSZong Li init.parent_names = &pic->parent_name; 528c816e1ddSZong Li init.num_parents = 1; 529c816e1ddSZong Li init.ops = pic->ops; 530c816e1ddSZong Li pic->hw.init = &init; 531c816e1ddSZong Li 532c816e1ddSZong Li pic->pd = pd; 533c816e1ddSZong Li 534c816e1ddSZong Li if (pic->pwd) 535732374a0SPragnesh Patel __prci_wrpll_read_cfg0(pd, pic->pwd); 536c816e1ddSZong Li 537c816e1ddSZong Li r = devm_clk_hw_register(dev, &pic->hw); 538c816e1ddSZong Li if (r) { 539c816e1ddSZong Li dev_warn(dev, "Failed to register clock %s: %d\n", 540c816e1ddSZong Li init.name, r); 541c816e1ddSZong Li return r; 542c816e1ddSZong Li } 543c816e1ddSZong Li 544c816e1ddSZong Li r = clk_hw_register_clkdev(&pic->hw, pic->name, dev_name(dev)); 545c816e1ddSZong Li if (r) { 546c816e1ddSZong Li dev_warn(dev, "Failed to register clkdev for %s: %d\n", 547c816e1ddSZong Li init.name, r); 548c816e1ddSZong Li return r; 549c816e1ddSZong Li } 550c816e1ddSZong Li 551c816e1ddSZong Li pd->hw_clks.hws[i] = &pic->hw; 552c816e1ddSZong Li } 553c816e1ddSZong Li 554c816e1ddSZong Li pd->hw_clks.num = i; 555c816e1ddSZong Li 556c816e1ddSZong Li r = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, 557c816e1ddSZong Li &pd->hw_clks); 558c816e1ddSZong Li if (r) { 559c816e1ddSZong Li dev_err(dev, "could not add hw_provider: %d\n", r); 560c816e1ddSZong Li return r; 561c816e1ddSZong Li } 562c816e1ddSZong Li 563c816e1ddSZong Li return 0; 564c816e1ddSZong Li } 565c816e1ddSZong Li 566c816e1ddSZong Li /** 567c816e1ddSZong Li * sifive_prci_init() - initialize prci data and check parent count 568c816e1ddSZong Li * @pdev: platform device pointer for the prci 569c816e1ddSZong Li * 570c816e1ddSZong Li * Return: 0 upon success or a negative error code upon failure. 571c816e1ddSZong Li */ 572c816e1ddSZong Li static int sifive_prci_probe(struct platform_device *pdev) 573c816e1ddSZong Li { 574c816e1ddSZong Li struct device *dev = &pdev->dev; 575c816e1ddSZong Li struct resource *res; 576c816e1ddSZong Li struct __prci_data *pd; 577c816e1ddSZong Li const struct prci_clk_desc *desc; 578c816e1ddSZong Li int r; 579c816e1ddSZong Li 580c816e1ddSZong Li desc = of_device_get_match_data(&pdev->dev); 581c816e1ddSZong Li 582c816e1ddSZong Li pd = devm_kzalloc(dev, struct_size(pd, hw_clks.hws, desc->num_clks), GFP_KERNEL); 583c816e1ddSZong Li if (!pd) 584c816e1ddSZong Li return -ENOMEM; 585c816e1ddSZong Li 586c816e1ddSZong Li res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 587c816e1ddSZong Li pd->va = devm_ioremap_resource(dev, res); 588c816e1ddSZong Li if (IS_ERR(pd->va)) 589c816e1ddSZong Li return PTR_ERR(pd->va); 590c816e1ddSZong Li 591c816e1ddSZong Li r = __prci_register_clocks(dev, pd, desc); 592c816e1ddSZong Li if (r) { 593c816e1ddSZong Li dev_err(dev, "could not register clocks: %d\n", r); 594c816e1ddSZong Li return r; 595c816e1ddSZong Li } 596c816e1ddSZong Li 597c816e1ddSZong Li dev_dbg(dev, "SiFive PRCI probed\n"); 598c816e1ddSZong Li 599c816e1ddSZong Li return 0; 600c816e1ddSZong Li } 601c816e1ddSZong Li 602c816e1ddSZong Li static const struct of_device_id sifive_prci_of_match[] = { 603c816e1ddSZong Li {.compatible = "sifive,fu540-c000-prci", .data = &prci_clk_fu540}, 604efc91ae4SZong Li {.compatible = "sifive,fu740-c000-prci", .data = &prci_clk_fu740}, 605c816e1ddSZong Li {} 606c816e1ddSZong Li }; 607c816e1ddSZong Li 608c816e1ddSZong Li static struct platform_driver sifive_prci_driver = { 609c816e1ddSZong Li .driver = { 610c816e1ddSZong Li .name = "sifive-clk-prci", 611c816e1ddSZong Li .of_match_table = sifive_prci_of_match, 612c816e1ddSZong Li }, 613c816e1ddSZong Li .probe = sifive_prci_probe, 614c816e1ddSZong Li }; 615c816e1ddSZong Li 616c816e1ddSZong Li static int __init sifive_prci_init(void) 617c816e1ddSZong Li { 618c816e1ddSZong Li return platform_driver_register(&sifive_prci_driver); 619c816e1ddSZong Li } 620c816e1ddSZong Li core_initcall(sifive_prci_init); 621