--- zzzz-none-000/linux-3.10.107/drivers/clk/clk-si5351.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/clk/clk-si5351.c 2021-02-04 17:41:59.000000000 +0000 @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include #include @@ -68,16 +68,16 @@ struct si5351_hw_data *clkout; }; -static const char const *si5351_input_names[] = { +static const char * const si5351_input_names[] = { "xtal", "clkin" }; -static const char const *si5351_pll_names[] = { +static const char * const si5351_pll_names[] = { "plla", "pllb", "vxco" }; -static const char const *si5351_msynth_names[] = { +static const char * const si5351_msynth_names[] = { "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7" }; -static const char const *si5351_clkout_names[] = { +static const char * const si5351_clkout_names[] = { "clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7" }; @@ -207,7 +207,7 @@ return true; } -static struct regmap_config si5351_regmap_config = { +static const struct regmap_config si5351_regmap_config = { .reg_bits = 8, .val_bits = 8, .cache_type = REGCACHE_RBTREE, @@ -439,7 +439,7 @@ dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n", - __func__, __clk_get_name(hwdata->hw.clk), + __func__, clk_hw_get_name(hw), hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, parent_rate, (unsigned long)rate); @@ -497,7 +497,7 @@ dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: a = %lu, b = %lu, c = %lu, parent_rate = %lu, rate = %lu\n", - __func__, __clk_get_name(hwdata->hw.clk), a, b, c, + __func__, clk_hw_get_name(hw), a, b, c, *parent_rate, rate); return rate; @@ -521,7 +521,7 @@ dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, parent_rate = %lu, rate = %lu\n", - __func__, __clk_get_name(hwdata->hw.clk), + __func__, clk_hw_get_name(hw), hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, parent_rate, rate); @@ -552,7 +552,8 @@ * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c * MSx_P3[19:0] = c * - * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0 + * MS[6,7] are integer (P1) divide only, P1 = divide value, + * P2 and P3 are not applicable * * for 150MHz < fOUT <= 160MHz: * @@ -606,9 +607,6 @@ if (!hwdata->params.valid) si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params); - if (hwdata->params.p3 == 0) - return parent_rate; - /* * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3) * multisync6-7: fOUT = fIN / P1 @@ -616,6 +614,8 @@ rate = parent_rate; if (hwdata->num > 5) { m = hwdata->params.p1; + } else if (hwdata->params.p3 == 0) { + return parent_rate; } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) & SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) { m = 4; @@ -632,7 +632,7 @@ dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, m = %lu, parent_rate = %lu, rate = %lu\n", - __func__, __clk_get_name(hwdata->hw.clk), + __func__, clk_hw_get_name(hw), hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, m, parent_rate, (unsigned long)rate); @@ -663,7 +663,7 @@ divby4 = 1; /* multisync can set pll */ - if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) { + if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { /* * find largest integer divider for max * vco frequency and given target rate @@ -679,6 +679,16 @@ c = 1; *parent_rate = a * rate; + } else if (hwdata->num >= 6) { + /* determine the closest integer divider */ + a = DIV_ROUND_CLOSEST(*parent_rate, rate); + if (a < SI5351_MULTISYNTH_A_MIN) + a = SI5351_MULTISYNTH_A_MIN; + if (a > SI5351_MULTISYNTH67_A_MAX) + a = SI5351_MULTISYNTH67_A_MAX; + + b = 0; + c = 1; } else { unsigned long rfrac, denom; @@ -692,9 +702,7 @@ a = *parent_rate / rate; if (a < SI5351_MULTISYNTH_A_MIN) a = SI5351_MULTISYNTH_A_MIN; - if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX) - a = SI5351_MULTISYNTH67_A_MAX; - else if (a > SI5351_MULTISYNTH_A_MAX) + if (a > SI5351_MULTISYNTH_A_MAX) a = SI5351_MULTISYNTH_A_MAX; /* find best approximation for b/c = fVCO mod fOUT */ @@ -723,6 +731,10 @@ hwdata->params.p3 = 1; hwdata->params.p2 = 0; hwdata->params.p1 = 0; + } else if (hwdata->num >= 6) { + hwdata->params.p3 = 0; + hwdata->params.p2 = 0; + hwdata->params.p1 = a; } else { hwdata->params.p3 = c; hwdata->params.p2 = (128 * b) % c; @@ -733,7 +745,7 @@ dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: a = %lu, b = %lu, c = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n", - __func__, __clk_get_name(hwdata->hw.clk), a, b, c, divby4, + __func__, clk_hw_get_name(hw), a, b, c, divby4, *parent_rate, rate); return rate; @@ -765,7 +777,7 @@ dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: p1 = %lu, p2 = %lu, p3 = %lu, divby4 = %d, parent_rate = %lu, rate = %lu\n", - __func__, __clk_get_name(hwdata->hw.clk), + __func__, clk_hw_get_name(hw), hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, divby4, parent_rate, rate); @@ -851,6 +863,41 @@ return 0; } +static int _si5351_clkout_set_disable_state( + struct si5351_driver_data *drvdata, int num, + enum si5351_disable_state state) +{ + u8 reg = (num < 4) ? SI5351_CLK3_0_DISABLE_STATE : + SI5351_CLK7_4_DISABLE_STATE; + u8 shift = (num < 4) ? (2 * num) : (2 * (num-4)); + u8 mask = SI5351_CLK_DISABLE_STATE_MASK << shift; + u8 val; + + if (num > 8) + return -EINVAL; + + switch (state) { + case SI5351_DISABLE_LOW: + val = SI5351_CLK_DISABLE_STATE_LOW; + break; + case SI5351_DISABLE_HIGH: + val = SI5351_CLK_DISABLE_STATE_HIGH; + break; + case SI5351_DISABLE_FLOATING: + val = SI5351_CLK_DISABLE_STATE_FLOAT; + break; + case SI5351_DISABLE_NEVER: + val = SI5351_CLK_DISABLE_STATE_NEVER; + break; + default: + return 0; + } + + si5351_set_bits(drvdata, reg, mask, val << shift); + + return 0; +} + static int si5351_clkout_prepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = @@ -966,7 +1013,7 @@ rate = SI5351_CLKOUT_MIN_FREQ; /* request frequency if multisync master */ - if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) { + if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) { /* use r divider for frequencies below 1MHz */ rdiv = SI5351_OUTPUT_CLK_DIV_1; while (rate < SI5351_MULTISYNTH_MIN_FREQ && @@ -995,7 +1042,7 @@ dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", - __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv), + __func__, clk_hw_get_name(hw), (1 << rdiv), *parent_rate, rate); return rate; @@ -1046,7 +1093,7 @@ dev_dbg(&hwdata->drvdata->client->dev, "%s - %s: rdiv = %u, parent_rate = %lu, rate = %lu\n", - __func__, __clk_get_name(hwdata->hw.clk), (1 << rdiv), + __func__, clk_hw_get_name(hw), (1 << rdiv), parent_rate, rate); return 0; @@ -1076,11 +1123,11 @@ }; MODULE_DEVICE_TABLE(of, si5351_dt_ids); -static int si5351_dt_parse(struct i2c_client *client) +static int si5351_dt_parse(struct i2c_client *client, + enum si5351_variant variant) { struct device_node *child, *np = client->dev.of_node; struct si5351_platform_data *pdata; - const struct of_device_id *match; struct property *prop; const __be32 *p; int num = 0; @@ -1089,22 +1136,10 @@ if (np == NULL) return 0; - match = of_match_node(si5351_dt_ids, np); - if (match == NULL) - return -EINVAL; - pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - pdata->variant = (enum si5351_variant)match->data; - pdata->clk_xtal = of_clk_get(np, 0); - if (!IS_ERR(pdata->clk_xtal)) - clk_put(pdata->clk_xtal); - pdata->clk_clkin = of_clk_get(np, 1); - if (!IS_ERR(pdata->clk_clkin)) - clk_put(pdata->clk_clkin); - /* * property silabs,pll-source : , [<..>] * allow to selectively set pll source @@ -1128,7 +1163,7 @@ pdata->pll_src[num] = SI5351_PLL_SRC_XTAL; break; case 1: - if (pdata->variant != SI5351_VARIANT_C) { + if (variant != SI5351_VARIANT_C) { dev_err(&client->dev, "invalid parent %d for pll %d\n", val, num); @@ -1148,13 +1183,13 @@ if (of_property_read_u32(child, "reg", &num)) { dev_err(&client->dev, "missing reg property of %s\n", child->name); - return -EINVAL; + goto put_child; } if (num >= 8 || - (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) { + (variant == SI5351_VARIANT_A3 && num >= 3)) { dev_err(&client->dev, "invalid clkout %d\n", num); - return -EINVAL; + goto put_child; } if (!of_property_read_u32(child, "silabs,multisynth-source", @@ -1172,7 +1207,7 @@ dev_err(&client->dev, "invalid parent %d for multisynth %d\n", val, num); - return -EINVAL; + goto put_child; } } @@ -1191,11 +1226,11 @@ SI5351_CLKOUT_SRC_XTAL; break; case 3: - if (pdata->variant != SI5351_VARIANT_C) { + if (variant != SI5351_VARIANT_C) { dev_err(&client->dev, "invalid parent %d for clkout %d\n", val, num); - return -EINVAL; + goto put_child; } pdata->clkout[num].clkout_src = SI5351_CLKOUT_SRC_CLKIN; @@ -1204,7 +1239,7 @@ dev_err(&client->dev, "invalid parent %d for clkout %d\n", val, num); - return -EINVAL; + goto put_child; } } @@ -1221,7 +1256,34 @@ dev_err(&client->dev, "invalid drive strength %d for clkout %d\n", val, num); - return -EINVAL; + goto put_child; + } + } + + if (!of_property_read_u32(child, "silabs,disable-state", + &val)) { + switch (val) { + case 0: + pdata->clkout[num].disable_state = + SI5351_DISABLE_LOW; + break; + case 1: + pdata->clkout[num].disable_state = + SI5351_DISABLE_HIGH; + break; + case 2: + pdata->clkout[num].disable_state = + SI5351_DISABLE_FLOATING; + break; + case 3: + pdata->clkout[num].disable_state = + SI5351_DISABLE_NEVER; + break; + default: + dev_err(&client->dev, + "invalid disable state %d for clkout %d\n", + val, num); + goto put_child; } } @@ -1234,9 +1296,12 @@ client->dev.platform_data = pdata; return 0; +put_child: + of_node_put(child); + return -EINVAL; } #else -static int si5351_dt_parse(struct i2c_client *client) +static int si5351_dt_parse(struct i2c_client *client, enum si5351_variant variant) { return 0; } @@ -1245,6 +1310,7 @@ static int si5351_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { + enum si5351_variant variant = (enum si5351_variant)id->driver_data; struct si5351_platform_data *pdata; struct si5351_driver_data *drvdata; struct clk_init_data init; @@ -1253,7 +1319,7 @@ u8 num_parents, num_clocks; int ret, n; - ret = si5351_dt_parse(client); + ret = si5351_dt_parse(client, variant); if (ret) return ret; @@ -1269,9 +1335,23 @@ i2c_set_clientdata(client, drvdata); drvdata->client = client; - drvdata->variant = pdata->variant; - drvdata->pxtal = pdata->clk_xtal; - drvdata->pclkin = pdata->clk_clkin; + drvdata->variant = variant; + drvdata->pxtal = devm_clk_get(&client->dev, "xtal"); + drvdata->pclkin = devm_clk_get(&client->dev, "clkin"); + + if (PTR_ERR(drvdata->pxtal) == -EPROBE_DEFER || + PTR_ERR(drvdata->pclkin) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + /* + * Check for valid parent clock: VARIANT_A and VARIANT_B need XTAL, + * VARIANT_C can have CLKIN instead. + */ + if (IS_ERR(drvdata->pxtal) && + (drvdata->variant != SI5351_VARIANT_C || IS_ERR(drvdata->pclkin))) { + dev_err(&client->dev, "missing parent clock\n"); + return -EINVAL; + } drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config); if (IS_ERR(drvdata->regmap)) { @@ -1281,9 +1361,6 @@ /* Disable interrupts */ si5351_reg_write(drvdata, SI5351_INTERRUPT_MASK, 0xf0); - /* Set disabled output drivers to drive low */ - si5351_reg_write(drvdata, SI5351_CLK3_0_DISABLE_STATE, 0x00); - si5351_reg_write(drvdata, SI5351_CLK7_4_DISABLE_STATE, 0x00); /* Ensure pll select is on XTAL for Si5351A/B */ if (drvdata->variant != SI5351_VARIANT_C) si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, @@ -1327,8 +1404,22 @@ n, pdata->clkout[n].drive); return ret; } + + ret = _si5351_clkout_set_disable_state(drvdata, n, + pdata->clkout[n].disable_state); + if (ret) { + dev_err(&client->dev, + "failed set disable state of clkout%d to %d\n", + n, pdata->clkout[n].disable_state); + return ret; + } } + 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]; @@ -1343,7 +1434,8 @@ clk = devm_clk_register(&client->dev, &drvdata->xtal); if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return PTR_ERR(clk); + ret = PTR_ERR(clk); + goto err_clk; } /* register clkin input clock gate */ @@ -1361,7 +1453,8 @@ if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return PTR_ERR(clk); + ret = PTR_ERR(clk); + goto err_clk; } } @@ -1383,7 +1476,8 @@ clk = devm_clk_register(&client->dev, &drvdata->pll[0].hw); if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return -EINVAL; + ret = PTR_ERR(clk); + goto err_clk; } /* register PLLB or VXCO (Si5351B) */ @@ -1407,7 +1501,8 @@ clk = devm_clk_register(&client->dev, &drvdata->pll[1].hw); if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return -EINVAL; + ret = PTR_ERR(clk); + goto err_clk; } /* register clk multisync and clk out divider */ @@ -1428,8 +1523,10 @@ num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL); if (WARN_ON(!drvdata->msynth || !drvdata->clkout || - !drvdata->onecell.clks)) - return -ENOMEM; + !drvdata->onecell.clks)) { + ret = -ENOMEM; + goto err_clk; + } for (n = 0; n < num_clocks; n++) { drvdata->msynth[n].num = n; @@ -1447,7 +1544,8 @@ if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return -EINVAL; + ret = PTR_ERR(clk); + goto err_clk; } } @@ -1474,7 +1572,8 @@ if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); - return -EINVAL; + ret = PTR_ERR(clk); + goto err_clk; } drvdata->onecell.clks[n] = clk; @@ -1493,14 +1592,24 @@ &drvdata->onecell); if (ret) { dev_err(&client->dev, "unable to add clk provider\n"); - return ret; + goto err_clk; } 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 const struct i2c_device_id si5351_i2c_ids[] = { - { "silabs,si5351", 0 }, + { "si5351a", SI5351_VARIANT_A }, + { "si5351a-msop", SI5351_VARIANT_A3 }, + { "si5351b", SI5351_VARIANT_B }, + { "si5351c", SI5351_VARIANT_C }, { } }; MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids);