aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/can
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/can')
-rw-r--r--drivers/net/can/mscan/mpc5xxx_can.c179
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
115static 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
256err_invalid:
257 dev_err(&ofdev->dev, "invalid clock source specification\n");
258 /* clock source rate could not get determined */
259 return 0;
260
261err_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
266err_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
272static 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
112struct mpc512x_clockctl { 283struct 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 */
243static u32 mpc512x_can_get_clock(struct platform_device *ofdev, 419static 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
250static const struct of_device_id mpc5xxx_can_table[]; 427static const struct of_device_id mpc5xxx_can_table[];
@@ -386,11 +563,13 @@ static int mpc5xxx_can_resume(struct platform_device *ofdev)
386static const struct mpc5xxx_can_data mpc5200_can_data = { 563static 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
391static const struct mpc5xxx_can_data mpc5121_can_data = { 569static 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
396static const struct of_device_id mpc5xxx_can_table[] = { 575static const struct of_device_id mpc5xxx_can_table[] = {