xref: /openbmc/linux/drivers/clk/imx/clk.c (revision a4a4069f)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/clk.h>
3 #include <linux/err.h>
4 #include <linux/io.h>
5 #include <linux/of.h>
6 #include <linux/slab.h>
7 #include <linux/spinlock.h>
8 #include "clk.h"
9 
10 #define CCM_CCDR			0x4
11 #define CCDR_MMDC_CH0_MASK		BIT(17)
12 #define CCDR_MMDC_CH1_MASK		BIT(16)
13 
14 DEFINE_SPINLOCK(imx_ccm_lock);
15 
16 void __init imx_mmdc_mask_handshake(void __iomem *ccm_base,
17 				    unsigned int chn)
18 {
19 	unsigned int reg;
20 
21 	reg = readl_relaxed(ccm_base + CCM_CCDR);
22 	reg |= chn == 0 ? CCDR_MMDC_CH0_MASK : CCDR_MMDC_CH1_MASK;
23 	writel_relaxed(reg, ccm_base + CCM_CCDR);
24 }
25 
26 void __init imx_check_clocks(struct clk *clks[], unsigned int count)
27 {
28 	unsigned i;
29 
30 	for (i = 0; i < count; i++)
31 		if (IS_ERR(clks[i]))
32 			pr_err("i.MX clk %u: register failed with %ld\n",
33 			       i, PTR_ERR(clks[i]));
34 }
35 
36 void imx_check_clk_hws(struct clk_hw *clks[], unsigned int count)
37 {
38 	unsigned int i;
39 
40 	for (i = 0; i < count; i++)
41 		if (IS_ERR(clks[i]))
42 			pr_err("i.MX clk %u: register failed with %ld\n",
43 			       i, PTR_ERR(clks[i]));
44 }
45 
46 static struct clk * __init imx_obtain_fixed_clock_from_dt(const char *name)
47 {
48 	struct of_phandle_args phandle;
49 	struct clk *clk = ERR_PTR(-ENODEV);
50 	char *path;
51 
52 	path = kasprintf(GFP_KERNEL, "/clocks/%s", name);
53 	if (!path)
54 		return ERR_PTR(-ENOMEM);
55 
56 	phandle.np = of_find_node_by_path(path);
57 	kfree(path);
58 
59 	if (phandle.np) {
60 		clk = of_clk_get_from_provider(&phandle);
61 		of_node_put(phandle.np);
62 	}
63 	return clk;
64 }
65 
66 struct clk * __init imx_obtain_fixed_clock(
67 			const char *name, unsigned long rate)
68 {
69 	struct clk *clk;
70 
71 	clk = imx_obtain_fixed_clock_from_dt(name);
72 	if (IS_ERR(clk))
73 		clk = imx_clk_fixed(name, rate);
74 	return clk;
75 }
76 
77 struct clk_hw * __init imx_obtain_fixed_clock_hw(
78 			const char *name, unsigned long rate)
79 {
80 	struct clk *clk;
81 
82 	clk = imx_obtain_fixed_clock_from_dt(name);
83 	if (IS_ERR(clk))
84 		clk = imx_clk_fixed(name, rate);
85 	return __clk_get_hw(clk);
86 }
87 
88 struct clk_hw * __init imx_obtain_fixed_clk_hw(struct device_node *np,
89 					       const char *name)
90 {
91 	struct clk *clk;
92 
93 	clk = of_clk_get_by_name(np, name);
94 	if (IS_ERR(clk))
95 		return ERR_PTR(-ENOENT);
96 
97 	return __clk_get_hw(clk);
98 }
99 
100 /*
101  * This fixups the register CCM_CSCMR1 write value.
102  * The write/read/divider values of the aclk_podf field
103  * of that register have the relationship described by
104  * the following table:
105  *
106  * write value       read value        divider
107  * 3b'000            3b'110            7
108  * 3b'001            3b'111            8
109  * 3b'010            3b'100            5
110  * 3b'011            3b'101            6
111  * 3b'100            3b'010            3
112  * 3b'101            3b'011            4
113  * 3b'110            3b'000            1
114  * 3b'111            3b'001            2(default)
115  *
116  * That's why we do the xor operation below.
117  */
118 #define CSCMR1_FIXUP	0x00600000
119 
120 void imx_cscmr1_fixup(u32 *val)
121 {
122 	*val ^= CSCMR1_FIXUP;
123 	return;
124 }
125 
126 static int imx_keep_uart_clocks __initdata;
127 static struct clk ** const *imx_uart_clocks __initdata;
128 
129 static int __init imx_keep_uart_clocks_param(char *str)
130 {
131 	imx_keep_uart_clocks = 1;
132 
133 	return 0;
134 }
135 __setup_param("earlycon", imx_keep_uart_earlycon,
136 	      imx_keep_uart_clocks_param, 0);
137 __setup_param("earlyprintk", imx_keep_uart_earlyprintk,
138 	      imx_keep_uart_clocks_param, 0);
139 
140 void __init imx_register_uart_clocks(struct clk ** const clks[])
141 {
142 	if (imx_keep_uart_clocks) {
143 		int i;
144 
145 		imx_uart_clocks = clks;
146 		for (i = 0; imx_uart_clocks[i]; i++)
147 			clk_prepare_enable(*imx_uart_clocks[i]);
148 	}
149 }
150 
151 static int __init imx_clk_disable_uart(void)
152 {
153 	if (imx_keep_uart_clocks && imx_uart_clocks) {
154 		int i;
155 
156 		for (i = 0; imx_uart_clocks[i]; i++)
157 			clk_disable_unprepare(*imx_uart_clocks[i]);
158 	}
159 
160 	return 0;
161 }
162 late_initcall_sync(imx_clk_disable_uart);
163