/* * Either the oscillator clock (SYS_XTAL_IN) or the IP bus clock * (IP_CLK) can be selected as MSCAN clock source. According to * the MPC5200 user's manual, the oscillator clock is the better * choice as it has less jitter. For this reason, it is selected * by default. Unfortunately, it can not be selected for the old * MPC5200 Rev. A chips due to a hardware bug (check errata).
*/ if (clock_name && strcmp(clock_name, "ip") == 0)
*mscan_clksrc = MSCAN_CLKSRC_BUS; else
*mscan_clksrc = MSCAN_CLKSRC_XTAL;
freq = mpc5xxx_get_bus_frequency(&ofdev->dev); if (!freq) return 0;
if (*mscan_clksrc == MSCAN_CLKSRC_BUS || pvr == 0x80822011) return freq;
/* Determine SYS_XTAL_IN frequency from the clock domain settings */
np_cdm = of_find_matching_node(NULL, mpc52xx_cdm_ids); if (!np_cdm) {
dev_err(&ofdev->dev, "can't get clock node!\n"); return 0;
}
cdm = of_iomap(np_cdm, 0); if (!cdm) {
of_node_put(np_cdm);
dev_err(&ofdev->dev, "can't map clock node!\n"); return 0;
}
if (in_8(&cdm->ipb_clk_sel) & 0x1)
freq *= 2;
val = in_be32(&cdm->rstcfg);
/* the caller passed in the clock source spec that was read from * the device tree, get the optional clock divider as well
*/
np = ofdev->dev.of_node;
clockdiv = 1;
of_property_read_u32(np, "fsl,mscan-clock-divider", &clockdiv);
dev_dbg(&ofdev->dev, "device tree specs: clk src[%s] div[%d]\n",
clock_source ? clock_source : "", clockdiv);
/* when clock-source is 'ip', the CANCTL1[CLKSRC] bit needs to * get set, and the 'ips' clock is the input to the MSCAN * component * * for clock-source values of 'ref' or 'sys' the CANCTL1[CLKSRC] * bit needs to get cleared, an optional clock-divider may have * been specified (the default value is 1), the appropriate * MSCAN related MCLK is the input to the MSCAN component * * in the absence of a clock-source spec, first an optimal clock * gets determined based on the 'sys' clock, if that fails the * 'ref' clock is used
*/
clk_from = CLK_FROM_AUTO; if (clock_source) { /* interpret the device tree's spec for the clock source */ if (!strcmp(clock_source, "ip"))
clk_from = CLK_FROM_IPS; elseif (!strcmp(clock_source, "sys"))
clk_from = CLK_FROM_SYS; elseif (!strcmp(clock_source, "ref"))
clk_from = CLK_FROM_REF; else goto err_invalid;
dev_dbg(&ofdev->dev, "got a clk source spec[%d]\n", clk_from);
} if (clk_from == CLK_FROM_AUTO) { /* no spec so far, try the 'sys' clock; round to the * next MHz and see if we can get a multiple of 16MHz
*/
dev_dbg(&ofdev->dev, "no clk source spec, trying SYS\n");
clk_in = devm_clk_get(&ofdev->dev, "sys"); if (IS_ERR(clk_in)) goto err_notavail;
freq_calc = clk_get_rate(clk_in);
freq_calc += 499999;
freq_calc /= 1000000;
freq_calc *= 1000000; if ((freq_calc % 16000000) == 0) {
clk_from = CLK_FROM_SYS;
clockdiv = freq_calc / 16000000;
dev_dbg(&ofdev->dev, "clk fit, sys[%lu] div[%d] freq[%lu]\n",
freq_calc, clockdiv, freq_calc / clockdiv);
}
} if (clk_from == CLK_FROM_AUTO) { /* no spec so far, use the 'ref' clock */
dev_dbg(&ofdev->dev, "no clk source spec, trying REF\n");
clk_in = devm_clk_get(&ofdev->dev, "ref"); if (IS_ERR(clk_in)) goto err_notavail;
clk_from = CLK_FROM_REF;
freq_calc = clk_get_rate(clk_in);
dev_dbg(&ofdev->dev, "clk fit, ref[%lu] (no div) freq[%lu]\n",
freq_calc, freq_calc);
}
/* select IPS or MCLK as the MSCAN input (returned to the caller), * setup the MCLK mux source and rate if applicable, apply the * optionally specified or derived above divider, and determine * the actual resulting clock rate to return to the caller
*/ switch (clk_from) { case CLK_FROM_IPS:
clk_can = devm_clk_get(&ofdev->dev, "ips"); if (IS_ERR(clk_can)) goto err_notavail;
priv = netdev_priv(dev_get_drvdata(&ofdev->dev));
priv->clk_can = clk_can;
freq_calc = clk_get_rate(clk_can);
*mscan_clksrc = MSCAN_CLKSRC_IPS;
dev_dbg(&ofdev->dev, "clk from IPS, clksrc[%d] freq[%lu]\n",
*mscan_clksrc, freq_calc); break; case CLK_FROM_SYS: case CLK_FROM_REF:
clk_can = devm_clk_get(&ofdev->dev, "mclk"); if (IS_ERR(clk_can)) goto err_notavail;
priv = netdev_priv(dev_get_drvdata(&ofdev->dev));
priv->clk_can = clk_can; if (clk_from == CLK_FROM_SYS)
clk_in = devm_clk_get(&ofdev->dev, "sys"); if (clk_from == CLK_FROM_REF)
clk_in = devm_clk_get(&ofdev->dev, "ref"); if (IS_ERR(clk_in)) goto err_notavail;
clk_set_parent(clk_can, clk_in);
freq_calc = clk_get_rate(clk_in);
freq_calc /= clockdiv;
clk_set_rate(clk_can, freq_calc);
freq_calc = clk_get_rate(clk_can);
*mscan_clksrc = MSCAN_CLKSRC_BUS;
dev_dbg(&ofdev->dev, "clk from MCLK, clksrc[%d] freq[%lu]\n",
*mscan_clksrc, freq_calc); break; default: goto err_invalid;
}
/* the above clk_can item is used for the bitrate, access to * the peripheral's register set needs the clk_ipg item
*/
clk_ipg = devm_clk_get(&ofdev->dev, "ipg"); if (IS_ERR(clk_ipg)) goto err_notavail_ipg; if (clk_prepare_enable(clk_ipg)) goto err_notavail_ipg;
priv = netdev_priv(dev_get_drvdata(&ofdev->dev));
priv->clk_ipg = clk_ipg;
/* return the determined clock source rate */ return freq_calc;
err_invalid:
dev_err(&ofdev->dev, "invalid clock source specification\n"); /* clock source rate could not get determined */ return 0;
err_notavail:
dev_err(&ofdev->dev, "cannot acquire or setup bitrate clock source\n"); /* clock source rate could not get determined */ return 0;
err_notavail_ipg:
dev_err(&ofdev->dev, "cannot acquire or setup register clock\n"); /* clock source rate could not get determined */ return 0;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.