diff options
Diffstat (limited to 'drivers/net/can')
-rw-r--r-- | drivers/net/can/mscan/mpc5xxx_can.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index e59b3a392af6..f48f1297ff30 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c | |||
@@ -109,6 +109,177 @@ static u32 mpc52xx_can_get_clock(struct platform_device *ofdev, | |||
109 | #endif /* CONFIG_PPC_MPC52xx */ | 109 | #endif /* CONFIG_PPC_MPC52xx */ |
110 | 110 | ||
111 | #ifdef CONFIG_PPC_MPC512x | 111 | #ifdef CONFIG_PPC_MPC512x |
112 | |||
113 | #if IS_ENABLED(CONFIG_COMMON_CLK) | ||
114 | |||
115 | static u32 mpc512x_can_get_clock(struct platform_device *ofdev, | ||
116 | const char *clock_source, int *mscan_clksrc) | ||
117 | { | ||
118 | struct device_node *np; | ||
119 | u32 clockdiv; | ||
120 | enum { | ||
121 | CLK_FROM_AUTO, | ||
122 | CLK_FROM_IPS, | ||
123 | CLK_FROM_SYS, | ||
124 | CLK_FROM_REF, | ||
125 | } clk_from; | ||
126 | struct clk *clk_in, *clk_can; | ||
127 | unsigned long freq_calc; | ||
128 | struct mscan_priv *priv; | ||
129 | struct clk *clk_ipg; | ||
130 | |||
131 | /* the caller passed in the clock source spec that was read from | ||
132 | * the device tree, get the optional clock divider as well | ||
133 | */ | ||
134 | np = ofdev->dev.of_node; | ||
135 | clockdiv = 1; | ||
136 | of_property_read_u32(np, "fsl,mscan-clock-divider", &clockdiv); | ||
137 | dev_dbg(&ofdev->dev, "device tree specs: clk src[%s] div[%d]\n", | ||
138 | clock_source ? clock_source : "<NULL>", clockdiv); | ||
139 | |||
140 | /* when clock-source is 'ip', the CANCTL1[CLKSRC] bit needs to | ||
141 | * get set, and the 'ips' clock is the input to the MSCAN | ||
142 | * component | ||
143 | * | ||
144 | * for clock-source values of 'ref' or 'sys' the CANCTL1[CLKSRC] | ||
145 | * bit needs to get cleared, an optional clock-divider may have | ||
146 | * been specified (the default value is 1), the appropriate | ||
147 | * MSCAN related MCLK is the input to the MSCAN component | ||
148 | * | ||
149 | * in the absence of a clock-source spec, first an optimal clock | ||
150 | * gets determined based on the 'sys' clock, if that fails the | ||
151 | * 'ref' clock is used | ||
152 | */ | ||
153 | clk_from = CLK_FROM_AUTO; | ||
154 | if (clock_source) { | ||
155 | /* interpret the device tree's spec for the clock source */ | ||
156 | if (!strcmp(clock_source, "ip")) | ||
157 | clk_from = CLK_FROM_IPS; | ||
158 | else if (!strcmp(clock_source, "sys")) | ||
159 | clk_from = CLK_FROM_SYS; | ||
160 | else if (!strcmp(clock_source, "ref")) | ||
161 | clk_from = CLK_FROM_REF; | ||
162 | else | ||
163 | goto err_invalid; | ||
164 | dev_dbg(&ofdev->dev, "got a clk source spec[%d]\n", clk_from); | ||
165 | } | ||
166 | if (clk_from == CLK_FROM_AUTO) { | ||
167 | /* no spec so far, try the 'sys' clock; round to the | ||
168 | * next MHz and see if we can get a multiple of 16MHz | ||
169 | */ | ||
170 | dev_dbg(&ofdev->dev, "no clk source spec, trying SYS\n"); | ||
171 | clk_in = devm_clk_get(&ofdev->dev, "sys"); | ||
172 | if (IS_ERR(clk_in)) | ||
173 | goto err_notavail; | ||
174 | freq_calc = clk_get_rate(clk_in); | ||
175 | freq_calc += 499999; | ||
176 | freq_calc /= 1000000; | ||
177 | freq_calc *= 1000000; | ||
178 | if ((freq_calc % 16000000) == 0) { | ||
179 | clk_from = CLK_FROM_SYS; | ||
180 | clockdiv = freq_calc / 16000000; | ||
181 | dev_dbg(&ofdev->dev, | ||
182 | "clk fit, sys[%lu] div[%d] freq[%lu]\n", | ||
183 | freq_calc, clockdiv, freq_calc / clockdiv); | ||
184 | } | ||
185 | } | ||
186 | if (clk_from == CLK_FROM_AUTO) { | ||
187 | /* no spec so far, use the 'ref' clock */ | ||
188 | dev_dbg(&ofdev->dev, "no clk source spec, trying REF\n"); | ||
189 | clk_in = devm_clk_get(&ofdev->dev, "ref"); | ||
190 | if (IS_ERR(clk_in)) | ||
191 | goto err_notavail; | ||
192 | clk_from = CLK_FROM_REF; | ||
193 | freq_calc = clk_get_rate(clk_in); | ||
194 | dev_dbg(&ofdev->dev, | ||
195 | "clk fit, ref[%lu] (no div) freq[%lu]\n", | ||
196 | freq_calc, freq_calc); | ||
197 | } | ||
198 | |||
199 | /* select IPS or MCLK as the MSCAN input (returned to the caller), | ||
200 | * setup the MCLK mux source and rate if applicable, apply the | ||
201 | * optionally specified or derived above divider, and determine | ||
202 | * the actual resulting clock rate to return to the caller | ||
203 | */ | ||
204 | switch (clk_from) { | ||
205 | case CLK_FROM_IPS: | ||
206 | clk_can = devm_clk_get(&ofdev->dev, "ips"); | ||
207 | if (IS_ERR(clk_can)) | ||
208 | goto err_notavail; | ||
209 | priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); | ||
210 | priv->clk_can = clk_can; | ||
211 | freq_calc = clk_get_rate(clk_can); | ||
212 | *mscan_clksrc = MSCAN_CLKSRC_IPS; | ||
213 | dev_dbg(&ofdev->dev, "clk from IPS, clksrc[%d] freq[%lu]\n", | ||
214 | *mscan_clksrc, freq_calc); | ||
215 | break; | ||
216 | case CLK_FROM_SYS: | ||
217 | case CLK_FROM_REF: | ||
218 | clk_can = devm_clk_get(&ofdev->dev, "mclk"); | ||
219 | if (IS_ERR(clk_can)) | ||
220 | goto err_notavail; | ||
221 | priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); | ||
222 | priv->clk_can = clk_can; | ||
223 | if (clk_from == CLK_FROM_SYS) | ||
224 | clk_in = devm_clk_get(&ofdev->dev, "sys"); | ||
225 | if (clk_from == CLK_FROM_REF) | ||
226 | clk_in = devm_clk_get(&ofdev->dev, "ref"); | ||
227 | if (IS_ERR(clk_in)) | ||
228 | goto err_notavail; | ||
229 | clk_set_parent(clk_can, clk_in); | ||
230 | freq_calc = clk_get_rate(clk_in); | ||
231 | freq_calc /= clockdiv; | ||
232 | clk_set_rate(clk_can, freq_calc); | ||
233 | freq_calc = clk_get_rate(clk_can); | ||
234 | *mscan_clksrc = MSCAN_CLKSRC_BUS; | ||
235 | dev_dbg(&ofdev->dev, "clk from MCLK, clksrc[%d] freq[%lu]\n", | ||
236 | *mscan_clksrc, freq_calc); | ||
237 | break; | ||
238 | default: | ||
239 | goto err_invalid; | ||
240 | } | ||
241 | |||
242 | /* the above clk_can item is used for the bitrate, access to | ||
243 | * the peripheral's register set needs the clk_ipg item | ||
244 | */ | ||
245 | clk_ipg = devm_clk_get(&ofdev->dev, "ipg"); | ||
246 | if (IS_ERR(clk_ipg)) | ||
247 | goto err_notavail_ipg; | ||
248 | if (clk_prepare_enable(clk_ipg)) | ||
249 | goto err_notavail_ipg; | ||
250 | priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); | ||
251 | priv->clk_ipg = clk_ipg; | ||
252 | |||
253 | /* return the determined clock source rate */ | ||
254 | return freq_calc; | ||
255 | |||
256 | err_invalid: | ||
257 | dev_err(&ofdev->dev, "invalid clock source specification\n"); | ||
258 | /* clock source rate could not get determined */ | ||
259 | return 0; | ||
260 | |||
261 | err_notavail: | ||
262 | dev_err(&ofdev->dev, "cannot acquire or setup bitrate clock source\n"); | ||
263 | /* clock source rate could not get determined */ | ||
264 | return 0; | ||
265 | |||
266 | err_notavail_ipg: | ||
267 | dev_err(&ofdev->dev, "cannot acquire or setup register clock\n"); | ||
268 | /* clock source rate could not get determined */ | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static void mpc512x_can_put_clock(struct platform_device *ofdev) | ||
273 | { | ||
274 | struct mscan_priv *priv; | ||
275 | |||
276 | priv = netdev_priv(dev_get_drvdata(&ofdev->dev)); | ||
277 | if (priv->clk_ipg) | ||
278 | clk_disable_unprepare(priv->clk_ipg); | ||
279 | } | ||
280 | |||
281 | #else /* COMMON_CLK */ | ||
282 | |||
112 | struct mpc512x_clockctl { | 283 | struct mpc512x_clockctl { |
113 | u32 spmr; /* System PLL Mode Reg */ | 284 | u32 spmr; /* System PLL Mode Reg */ |
114 | u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */ | 285 | u32 sccr[2]; /* System Clk Ctrl Reg 1 & 2 */ |
@@ -239,12 +410,18 @@ exit_put: | |||
239 | of_node_put(np_clock); | 410 | of_node_put(np_clock); |
240 | return freq; | 411 | return freq; |
241 | } | 412 | } |
413 | |||
414 | #define mpc512x_can_put_clock NULL | ||
415 | |||
416 | #endif /* COMMON_CLK */ | ||
417 | |||
242 | #else /* !CONFIG_PPC_MPC512x */ | 418 | #else /* !CONFIG_PPC_MPC512x */ |
243 | static u32 mpc512x_can_get_clock(struct platform_device *ofdev, | 419 | static u32 mpc512x_can_get_clock(struct platform_device *ofdev, |
244 | const char *clock_name, int *mscan_clksrc) | 420 | const char *clock_name, int *mscan_clksrc) |
245 | { | 421 | { |
246 | return 0; | 422 | return 0; |
247 | } | 423 | } |
424 | #define mpc512x_can_put_clock NULL | ||
248 | #endif /* CONFIG_PPC_MPC512x */ | 425 | #endif /* CONFIG_PPC_MPC512x */ |
249 | 426 | ||
250 | static const struct of_device_id mpc5xxx_can_table[]; | 427 | static const struct of_device_id mpc5xxx_can_table[]; |
@@ -386,11 +563,13 @@ static int mpc5xxx_can_resume(struct platform_device *ofdev) | |||
386 | static const struct mpc5xxx_can_data mpc5200_can_data = { | 563 | static const struct mpc5xxx_can_data mpc5200_can_data = { |
387 | .type = MSCAN_TYPE_MPC5200, | 564 | .type = MSCAN_TYPE_MPC5200, |
388 | .get_clock = mpc52xx_can_get_clock, | 565 | .get_clock = mpc52xx_can_get_clock, |
566 | /* .put_clock not applicable */ | ||
389 | }; | 567 | }; |
390 | 568 | ||
391 | static const struct mpc5xxx_can_data mpc5121_can_data = { | 569 | static const struct mpc5xxx_can_data mpc5121_can_data = { |
392 | .type = MSCAN_TYPE_MPC5121, | 570 | .type = MSCAN_TYPE_MPC5121, |
393 | .get_clock = mpc512x_can_get_clock, | 571 | .get_clock = mpc512x_can_get_clock, |
572 | .put_clock = mpc512x_can_put_clock, | ||
394 | }; | 573 | }; |
395 | 574 | ||
396 | static const struct of_device_id mpc5xxx_can_table[] = { | 575 | static const struct of_device_id mpc5xxx_can_table[] = { |