162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/bits.h> 362306a36Sopenharmony_ci#include <linux/clk.h> 462306a36Sopenharmony_ci#include <linux/clk-provider.h> 562306a36Sopenharmony_ci#include <linux/err.h> 662306a36Sopenharmony_ci#include <linux/io.h> 762306a36Sopenharmony_ci#include <linux/module.h> 862306a36Sopenharmony_ci#include <linux/of.h> 962306a36Sopenharmony_ci#include <linux/slab.h> 1062306a36Sopenharmony_ci#include <linux/spinlock.h> 1162306a36Sopenharmony_ci#include "clk.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define CCM_CCDR 0x4 1462306a36Sopenharmony_ci#define CCDR_MMDC_CH0_MASK BIT(17) 1562306a36Sopenharmony_ci#define CCDR_MMDC_CH1_MASK BIT(16) 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciDEFINE_SPINLOCK(imx_ccm_lock); 1862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_ccm_lock); 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cibool mcore_booted; 2162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(mcore_booted); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_civoid imx_unregister_hw_clocks(struct clk_hw *hws[], unsigned int count) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned int i; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci for (i = 0; i < count; i++) 2862306a36Sopenharmony_ci clk_hw_unregister(hws[i]); 2962306a36Sopenharmony_ci} 3062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_unregister_hw_clocks); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_civoid imx_mmdc_mask_handshake(void __iomem *ccm_base, 3362306a36Sopenharmony_ci unsigned int chn) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci unsigned int reg; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci reg = readl_relaxed(ccm_base + CCM_CCDR); 3862306a36Sopenharmony_ci reg |= chn == 0 ? CCDR_MMDC_CH0_MASK : CCDR_MMDC_CH1_MASK; 3962306a36Sopenharmony_ci writel_relaxed(reg, ccm_base + CCM_CCDR); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_civoid imx_check_clocks(struct clk *clks[], unsigned int count) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci unsigned i; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci for (i = 0; i < count; i++) 4762306a36Sopenharmony_ci if (IS_ERR(clks[i])) 4862306a36Sopenharmony_ci pr_err("i.MX clk %u: register failed with %ld\n", 4962306a36Sopenharmony_ci i, PTR_ERR(clks[i])); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_civoid imx_check_clk_hws(struct clk_hw *clks[], unsigned int count) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci unsigned int i; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci for (i = 0; i < count; i++) 5762306a36Sopenharmony_ci if (IS_ERR(clks[i])) 5862306a36Sopenharmony_ci pr_err("i.MX clk %u: register failed with %ld\n", 5962306a36Sopenharmony_ci i, PTR_ERR(clks[i])); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_check_clk_hws); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct clk *imx_obtain_fixed_clock_from_dt(const char *name) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct of_phandle_args phandle; 6662306a36Sopenharmony_ci struct clk *clk = ERR_PTR(-ENODEV); 6762306a36Sopenharmony_ci char *path; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci path = kasprintf(GFP_KERNEL, "/clocks/%s", name); 7062306a36Sopenharmony_ci if (!path) 7162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci phandle.np = of_find_node_by_path(path); 7462306a36Sopenharmony_ci kfree(path); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (phandle.np) { 7762306a36Sopenharmony_ci clk = of_clk_get_from_provider(&phandle); 7862306a36Sopenharmony_ci of_node_put(phandle.np); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci return clk; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct clk *imx_obtain_fixed_clock( 8462306a36Sopenharmony_ci const char *name, unsigned long rate) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct clk *clk; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci clk = imx_obtain_fixed_clock_from_dt(name); 8962306a36Sopenharmony_ci if (IS_ERR(clk)) 9062306a36Sopenharmony_ci clk = imx_clk_fixed(name, rate); 9162306a36Sopenharmony_ci return clk; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistruct clk_hw *imx_obtain_fixed_clock_hw( 9562306a36Sopenharmony_ci const char *name, unsigned long rate) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct clk *clk; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci clk = imx_obtain_fixed_clock_from_dt(name); 10062306a36Sopenharmony_ci if (IS_ERR(clk)) 10162306a36Sopenharmony_ci clk = imx_clk_fixed(name, rate); 10262306a36Sopenharmony_ci return __clk_get_hw(clk); 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct clk_hw *imx_obtain_fixed_of_clock(struct device_node *np, 10662306a36Sopenharmony_ci const char *name, unsigned long rate) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct clk *clk = of_clk_get_by_name(np, name); 10962306a36Sopenharmony_ci struct clk_hw *hw; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (IS_ERR(clk)) 11262306a36Sopenharmony_ci hw = imx_obtain_fixed_clock_hw(name, rate); 11362306a36Sopenharmony_ci else 11462306a36Sopenharmony_ci hw = __clk_get_hw(clk); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return hw; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistruct clk_hw *imx_get_clk_hw_by_name(struct device_node *np, const char *name) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct clk *clk; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci clk = of_clk_get_by_name(np, name); 12462306a36Sopenharmony_ci if (IS_ERR(clk)) 12562306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return __clk_get_hw(clk); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(imx_get_clk_hw_by_name); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci/* 13262306a36Sopenharmony_ci * This fixups the register CCM_CSCMR1 write value. 13362306a36Sopenharmony_ci * The write/read/divider values of the aclk_podf field 13462306a36Sopenharmony_ci * of that register have the relationship described by 13562306a36Sopenharmony_ci * the following table: 13662306a36Sopenharmony_ci * 13762306a36Sopenharmony_ci * write value read value divider 13862306a36Sopenharmony_ci * 3b'000 3b'110 7 13962306a36Sopenharmony_ci * 3b'001 3b'111 8 14062306a36Sopenharmony_ci * 3b'010 3b'100 5 14162306a36Sopenharmony_ci * 3b'011 3b'101 6 14262306a36Sopenharmony_ci * 3b'100 3b'010 3 14362306a36Sopenharmony_ci * 3b'101 3b'011 4 14462306a36Sopenharmony_ci * 3b'110 3b'000 1 14562306a36Sopenharmony_ci * 3b'111 3b'001 2(default) 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * That's why we do the xor operation below. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_ci#define CSCMR1_FIXUP 0x00600000 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid imx_cscmr1_fixup(u32 *val) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci *val ^= CSCMR1_FIXUP; 15462306a36Sopenharmony_ci return; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci#ifndef MODULE 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic bool imx_keep_uart_clocks; 16062306a36Sopenharmony_cistatic int imx_enabled_uart_clocks; 16162306a36Sopenharmony_cistatic struct clk **imx_uart_clocks; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int __init imx_keep_uart_clocks_param(char *str) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci imx_keep_uart_clocks = 1; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci__setup_param("earlycon", imx_keep_uart_earlycon, 17062306a36Sopenharmony_ci imx_keep_uart_clocks_param, 0); 17162306a36Sopenharmony_ci__setup_param("earlyprintk", imx_keep_uart_earlyprintk, 17262306a36Sopenharmony_ci imx_keep_uart_clocks_param, 0); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_civoid imx_register_uart_clocks(void) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci unsigned int num __maybe_unused; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci imx_enabled_uart_clocks = 0; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci/* i.MX boards use device trees now. For build tests without CONFIG_OF, do nothing */ 18162306a36Sopenharmony_ci#ifdef CONFIG_OF 18262306a36Sopenharmony_ci if (imx_keep_uart_clocks) { 18362306a36Sopenharmony_ci int i; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci num = of_clk_get_parent_count(of_stdout); 18662306a36Sopenharmony_ci if (!num) 18762306a36Sopenharmony_ci return; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (!of_stdout) 19062306a36Sopenharmony_ci return; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci imx_uart_clocks = kcalloc(num, sizeof(struct clk *), GFP_KERNEL); 19362306a36Sopenharmony_ci if (!imx_uart_clocks) 19462306a36Sopenharmony_ci return; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci for (i = 0; i < num; i++) { 19762306a36Sopenharmony_ci imx_uart_clocks[imx_enabled_uart_clocks] = of_clk_get(of_stdout, i); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Stop if there are no more of_stdout references */ 20062306a36Sopenharmony_ci if (IS_ERR(imx_uart_clocks[imx_enabled_uart_clocks])) 20162306a36Sopenharmony_ci return; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Only enable the clock if it's not NULL */ 20462306a36Sopenharmony_ci if (imx_uart_clocks[imx_enabled_uart_clocks]) 20562306a36Sopenharmony_ci clk_prepare_enable(imx_uart_clocks[imx_enabled_uart_clocks++]); 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci#endif 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int __init imx_clk_disable_uart(void) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci if (imx_keep_uart_clocks && imx_enabled_uart_clocks) { 21462306a36Sopenharmony_ci int i; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci for (i = 0; i < imx_enabled_uart_clocks; i++) { 21762306a36Sopenharmony_ci clk_disable_unprepare(imx_uart_clocks[i]); 21862306a36Sopenharmony_ci clk_put(imx_uart_clocks[i]); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci kfree(imx_uart_clocks); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_cilate_initcall_sync(imx_clk_disable_uart); 22762306a36Sopenharmony_ci#endif 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 230