/* * Driver for TI Multi PLL CDCE913/925/937/949 clock synthesizer * * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1, * Y4/Y5 to PLL2, and so on. PLL frequency is set on a first-come-first-serve * basis. Clients can directly request any frequency that the chip can * deliver using the standard clk framework. In addition, the device can * be configured and activated via the devicetree. * * Copyright (C) 2014, Topic Embedded Products * Licenced under GPL
*/ #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/gcd.h>
/* Each chip has different number of PLLs and outputs, for example: * The CECE925 has 2 PLLs which can be routed through dividers to 5 outputs. * Model this as 2 PLL clocks which are parents to the outputs.
*/
struct clk_cdce925_chip_info { int num_plls; int num_outputs;
};
staticunsignedlong cdce925_pll_calculate_rate(unsignedlong parent_rate,
u16 n, u16 m)
{ if ((!m || !n) || (m == n)) return parent_rate; /* In bypass mode runs at same frequency */ return mult_frac(parent_rate, (unsignedlong)n, (unsignedlong)m);
}
staticunsignedlong cdce925_pll_recalc_rate(struct clk_hw *hw, unsignedlong parent_rate)
{ /* Output frequency of PLL is Fout = (Fin/Pdiv)*(N/M) */ struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw);
if (rate <= parent_rate) { /* Can always deliver parent_rate in bypass mode */
*n = 0;
*m = 0;
} else { /* In PLL mode, need to apply min/max range */ if (rate < CDCE925_PLL_FREQUENCY_MIN)
rate = CDCE925_PLL_FREQUENCY_MIN; elseif (rate > CDCE925_PLL_FREQUENCY_MAX)
rate = CDCE925_PLL_FREQUENCY_MAX;
g = gcd(rate, parent_rate);
um = parent_rate / g;
un = rate / g; /* When outside hw range, reduce to fit (rounding errors) */ while ((un > 4095) || (um > 511)) {
un >>= 1;
um >>= 1;
} if (un == 0)
un = 1; if (um == 0)
um = 1;
*n = un;
*m = um;
}
}
staticlong cdce925_pll_round_rate(struct clk_hw *hw, unsignedlong rate, unsignedlong *parent_rate)
{
u16 n, m;
cdce925_pll_find_rate(rate, *parent_rate, &n, &m); return (long)cdce925_pll_calculate_rate(*parent_rate, n, m);
}
if (pll_rate <= 0) continue;
actual_rate = pll_rate / pdiv_now;
rate_error = abs((long)actual_rate - (long)rate); if (rate_error < best_rate_error) {
pdiv_best = pdiv_now;
best_rate_error = rate_error;
} /* TODO: Consider PLL frequency based on smaller n/m values
* and pick the better one if the error is equal */
}
if (idx >= ARRAY_SIZE(data->clk)) {
pr_err("%s: invalid index %u\n", __func__, idx); return ERR_PTR(-EINVAL);
}
return &data->clk[idx].hw;
}
staticint cdce925_regulator_enable(struct device *dev, constchar *name)
{ int err;
err = devm_regulator_get_enable(dev, name); if (err)
dev_err_probe(dev, err, "Failed to enable %s:\n", name);
return err;
}
/* The CDCE925 uses a funky way to read/write registers. Bulk mode is
* just weird, so just use the single byte mode exclusively. */ staticconststruct regmap_bus regmap_cdce925_bus = {
.write = cdce925_regmap_i2c_write,
.read = cdce925_regmap_i2c_read,
};
/* Register PLL clocks */ for (i = 0; i < data->chip_info->num_plls; ++i) {
pll_clk_name[i] = kasprintf(GFP_KERNEL, "%pOFn.pll%d",
client->dev.of_node, i); if (!pll_clk_name[i]) {
err = -ENOMEM; goto error;
}
init.name = pll_clk_name[i];
data->pll[i].chip = data;
data->pll[i].hw.init = &init;
data->pll[i].index = i;
err = devm_clk_hw_register(&client->dev, &data->pll[i].hw); if (err) {
dev_err(&client->dev, "Failed register PLL %d\n", i); goto error;
}
sprintf(child_name, "PLL%d", i+1);
np_output = of_get_child_by_name(node, child_name); if (!np_output) continue; if (!of_property_read_u32(np_output, "clock-frequency", &value)) {
err = clk_set_rate(data->pll[i].hw.clk, value); if (err)
dev_err(&client->dev, "unable to set PLL frequency %ud\n",
value);
} if (!of_property_read_u32(np_output, "spread-spectrum", &value)) {
u8 flag = of_property_read_bool(np_output, "spread-spectrum-center") ? 0x80 : 0x00;
regmap_update_bits(data->regmap,
0x16 + (i*CDCE925_OFFSET_PLL),
0x80, flag);
regmap_update_bits(data->regmap,
0x12 + (i*CDCE925_OFFSET_PLL),
0x07, value & 0x07);
}
of_node_put(np_output);
}
/* Register output clock Y1 */
init.ops = &cdce925_clk_y1_ops;
init.flags = 0;
init.num_parents = 1;
init.parent_names = &parent_name; /* Mux Y1 to input */
init.name = kasprintf(GFP_KERNEL, "%pOFn.Y1", client->dev.of_node); if (!init.name) {
err = -ENOMEM; goto error;
}
data->clk[0].chip = data;
data->clk[0].hw.init = &init;
data->clk[0].index = 0;
data->clk[0].pdiv = 1;
err = devm_clk_hw_register(&client->dev, &data->clk[0].hw);
kfree(init.name); /* clock framework made a copy of the name */ if (err) {
dev_err(&client->dev, "clock registration Y1 failed\n"); goto error;
}
/* Register output clocks Y2 .. Y5*/
init.ops = &cdce925_clk_ops;
init.flags = CLK_SET_RATE_PARENT;
init.num_parents = 1; for (i = 1; i < data->chip_info->num_outputs; ++i) {
init.name = kasprintf(GFP_KERNEL, "%pOFn.Y%d",
client->dev.of_node, i+1); if (!init.name) {
err = -ENOMEM; goto error;
}
data->clk[i].chip = data;
data->clk[i].hw.init = &init;
data->clk[i].index = i;
data->clk[i].pdiv = 1; switch (i) { case 1: case 2: /* Mux Y2/3 to PLL1 */
init.parent_names = &pll_clk_name[0]; break; case 3: case 4: /* Mux Y4/5 to PLL2 */
init.parent_names = &pll_clk_name[1]; break; case 5: case 6: /* Mux Y6/7 to PLL3 */
init.parent_names = &pll_clk_name[2]; break; case 7: case 8: /* Mux Y8/9 to PLL4 */
init.parent_names = &pll_clk_name[3]; break;
}
err = devm_clk_hw_register(&client->dev, &data->clk[i].hw);
kfree(init.name); /* clock framework made a copy of the name */ if (err) {
dev_err(&client->dev, "clock registration failed\n"); goto error;
}
}
/* Register the output clocks */
err = of_clk_add_hw_provider(client->dev.of_node, of_clk_cdce925_get,
data); if (err)
dev_err(&client->dev, "unable to add OF clock provider\n");
err = 0;
error: for (i = 0; i < data->chip_info->num_plls; ++i) /* clock framework made a copy of the name */
kfree(pll_clk_name[i]);
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.