diff options
Diffstat (limited to 'drivers/clk/clk-si5351.c')
-rw-r--r-- | drivers/clk/clk-si5351.c | 68 |
1 files changed, 48 insertions, 20 deletions
diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 20d90769cced..50e7c341e97e 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -72,7 +72,7 @@ static const char * const si5351_input_names[] = { "xtal", "clkin" }; static const char * const si5351_pll_names[] = { - "plla", "pllb", "vxco" + "si5351_plla", "si5351_pllb", "si5351_vxco" }; static const char * const si5351_msynth_names[] = { "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7" @@ -903,13 +903,42 @@ static int _si5351_clkout_set_disable_state( return 0; } +static void _si5351_clkout_reset_pll(struct si5351_driver_data *drvdata, int num) +{ + u8 val = si5351_reg_read(drvdata, SI5351_CLK0_CTRL + num); + + switch (val & SI5351_CLK_INPUT_MASK) { + case SI5351_CLK_INPUT_XTAL: + case SI5351_CLK_INPUT_CLKIN: + return; /* pll not used, no need to reset */ + } + + si5351_reg_write(drvdata, SI5351_PLL_RESET, + val & SI5351_CLK_PLL_SELECT ? SI5351_PLL_RESET_B : + SI5351_PLL_RESET_A); + + dev_dbg(&drvdata->client->dev, "%s - %s: pll = %d\n", + __func__, clk_hw_get_name(&drvdata->clkout[num].hw), + (val & SI5351_CLK_PLL_SELECT) ? 1 : 0); +} + static int si5351_clkout_prepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); + struct si5351_platform_data *pdata = + hwdata->drvdata->client->dev.platform_data; si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, SI5351_CLK_POWERDOWN, 0); + + /* + * Do a pll soft reset on the parent pll -- needed to get a + * deterministic phase relationship between the output clocks. + */ + if (pdata->clkout[hwdata->num].pll_reset) + _si5351_clkout_reset_pll(hwdata->drvdata, hwdata->num); + si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, (1 << hwdata->num), 0); return 0; @@ -1297,6 +1326,9 @@ static int si5351_dt_parse(struct i2c_client *client, pdata->clkout[num].pll_master = of_property_read_bool(child, "silabs,pll-master"); + + pdata->clkout[num].pll_reset = + of_property_read_bool(child, "silabs,pll-reset"); } client->dev.platform_data = pdata; @@ -1437,11 +1469,6 @@ static int si5351_i2c_probe(struct i2c_client *client, } } - if (!IS_ERR(drvdata->pxtal)) - clk_prepare_enable(drvdata->pxtal); - if (!IS_ERR(drvdata->pclkin)) - clk_prepare_enable(drvdata->pclkin); - /* register xtal input clock gate */ memset(&init, 0, sizeof(init)); init.name = si5351_input_names[0]; @@ -1456,7 +1483,7 @@ static int si5351_i2c_probe(struct i2c_client *client, ret = devm_clk_hw_register(&client->dev, &drvdata->xtal); if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } /* register clkin input clock gate */ @@ -1474,7 +1501,7 @@ static int si5351_i2c_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } } @@ -1496,7 +1523,7 @@ static int si5351_i2c_probe(struct i2c_client *client, ret = devm_clk_hw_register(&client->dev, &drvdata->pll[0].hw); if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } /* register PLLB or VXCO (Si5351B) */ @@ -1520,7 +1547,7 @@ static int si5351_i2c_probe(struct i2c_client *client, ret = devm_clk_hw_register(&client->dev, &drvdata->pll[1].hw); if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } /* register clk multisync and clk out divider */ @@ -1539,7 +1566,7 @@ static int si5351_i2c_probe(struct i2c_client *client, if (WARN_ON(!drvdata->msynth || !drvdata->clkout)) { ret = -ENOMEM; - goto err_clk; + return ret; } for (n = 0; n < num_clocks; n++) { @@ -1559,7 +1586,7 @@ static int si5351_i2c_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } } @@ -1587,7 +1614,7 @@ static int si5351_i2c_probe(struct i2c_client *client, if (ret) { dev_err(&client->dev, "unable to register %s\n", init.name); - goto err_clk; + return ret; } /* set initial clkout rate */ @@ -1606,17 +1633,17 @@ static int si5351_i2c_probe(struct i2c_client *client, drvdata); if (ret) { dev_err(&client->dev, "unable to add clk provider\n"); - goto err_clk; + return ret; } return 0; +} -err_clk: - if (!IS_ERR(drvdata->pxtal)) - clk_disable_unprepare(drvdata->pxtal); - if (!IS_ERR(drvdata->pclkin)) - clk_disable_unprepare(drvdata->pclkin); - return ret; +static int si5351_i2c_remove(struct i2c_client *client) +{ + of_clk_del_provider(client->dev.of_node); + + return 0; } static const struct i2c_device_id si5351_i2c_ids[] = { @@ -1634,6 +1661,7 @@ static struct i2c_driver si5351_driver = { .of_match_table = of_match_ptr(si5351_dt_ids), }, .probe = si5351_i2c_probe, + .remove = si5351_i2c_remove, .id_table = si5351_i2c_ids, }; module_i2c_driver(si5351_driver); |