Lines Matching +full:xtal +full:- +full:load +full:- +full:femtofarads

1 // SPDX-License-Identifier: GPL-2.0-or-later
10 * - Use spread spectrum
11 * - Use integer divider in FOD if applicable
15 #include <linux/clk-provider.h>
26 #include <dt-bindings/clock/versaclock.h>
31 /* Factory-reserved register block */
139 /* chip has built-in oscilator */
206 /* Factory reserved regs, make them read-only */ in vc5_regmap_is_writeable()
210 /* Factory reserved regs, make them read-only */ in vc5_regmap_is_writeable()
226 * VersaClock5 input multiplexer between XTAL and CLKIN divider
236 ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &src); in vc5_mux_get_parent()
248 dev_warn(&vc5->client->dev, in vc5_mux_get_parent()
260 if ((index > 1) || !vc5->clk_mux_ins) in vc5_mux_set_parent()
261 return -EINVAL; in vc5_mux_set_parent()
263 if (vc5->clk_mux_ins == (VC5_MUX_IN_CLKIN | VC5_MUX_IN_XIN)) { in vc5_mux_set_parent()
270 return -EINVAL; in vc5_mux_set_parent()
272 if (vc5->clk_mux_ins == VC5_MUX_IN_XIN) in vc5_mux_set_parent()
274 else if (vc5->clk_mux_ins == VC5_MUX_IN_CLKIN) in vc5_mux_set_parent()
277 return -EINVAL; in vc5_mux_set_parent()
280 return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, mask, src); in vc5_mux_set_parent()
297 ret = regmap_read(vc5->regmap, VC5_PRIM_SRC_SHDN, &premul); in vc5_dbl_recalc_rate()
313 return -EINVAL; in vc5_dbl_round_rate()
328 return regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, in vc5_dbl_set_rate()
347 ret = regmap_read(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, &prediv); in vc5_pfd_recalc_rate()
355 ret = regmap_read(vc5->regmap, VC5_REF_DIVIDER, &div); in vc5_pfd_recalc_rate()
373 return -EINVAL; in vc5_pfd_round_rate()
381 return -EINVAL; in vc5_pfd_round_rate()
397 ret = regmap_set_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, in vc5_pfd_set_rate()
402 return regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, 0x00); in vc5_pfd_set_rate()
407 /* We have dedicated div-2 predivider. */ in vc5_pfd_set_rate()
413 ret = regmap_update_bits(vc5->regmap, VC5_REF_DIVIDER, 0xff, div); in vc5_pfd_set_rate()
417 return regmap_clear_bits(vc5->regmap, VC5_VCO_CTRL_AND_PREDIV, in vc5_pfd_set_rate()
434 struct vc5_driver_data *vc5 = hwdata->vc5; in vc5_pll_recalc_rate()
438 regmap_bulk_read(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5); in vc5_pll_recalc_rate()
451 struct vc5_driver_data *vc5 = hwdata->vc5; in vc5_pll_round_rate()
455 rate = clamp(rate, VC5_PLL_VCO_MIN, vc5->chip_info->vco_max); in vc5_pll_round_rate()
464 div_frc *= BIT(24) - 1; in vc5_pll_round_rate()
467 hwdata->div_int = div_int; in vc5_pll_round_rate()
468 hwdata->div_frc = (u32)div_frc; in vc5_pll_round_rate()
477 struct vc5_driver_data *vc5 = hwdata->vc5; in vc5_pll_set_rate()
480 fb[0] = hwdata->div_int >> 4; in vc5_pll_set_rate()
481 fb[1] = hwdata->div_int << 4; in vc5_pll_set_rate()
482 fb[2] = hwdata->div_frc >> 16; in vc5_pll_set_rate()
483 fb[3] = hwdata->div_frc >> 8; in vc5_pll_set_rate()
484 fb[4] = hwdata->div_frc; in vc5_pll_set_rate()
486 return regmap_bulk_write(vc5->regmap, VC5_FEEDBACK_INT_DIV, fb, 5); in vc5_pll_set_rate()
499 struct vc5_driver_data *vc5 = hwdata->vc5; in vc5_fod_recalc_rate()
506 regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_INT(hwdata->num, 0), in vc5_fod_recalc_rate()
508 regmap_bulk_read(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0), in vc5_fod_recalc_rate()
536 * of the divider is 0xfff and fractional part is non-zero. in vc5_fod_round_rate()
549 hwdata->div_int = div_int; in vc5_fod_round_rate()
550 hwdata->div_frc = (u32)div_frc; in vc5_fod_round_rate()
559 struct vc5_driver_data *vc5 = hwdata->vc5; in vc5_fod_set_rate()
561 hwdata->div_frc >> 22, hwdata->div_frc >> 14, in vc5_fod_set_rate()
562 hwdata->div_frc >> 6, hwdata->div_frc << 2, in vc5_fod_set_rate()
565 hwdata->div_int >> 4, hwdata->div_int << 4, in vc5_fod_set_rate()
570 ret = regmap_bulk_write(vc5->regmap, VC5_OUT_DIV_FRAC(hwdata->num, 0), in vc5_fod_set_rate()
581 ret = regmap_clear_bits(vc5->regmap, VC5_GLOBAL_REGISTER, in vc5_fod_set_rate()
586 return regmap_set_bits(vc5->regmap, VC5_GLOBAL_REGISTER, in vc5_fod_set_rate()
599 struct vc5_driver_data *vc5 = hwdata->vc5; in vc5_clk_out_prepare()
614 if (vc5->chip_info->flags & VC5_HAS_BYPASS_SYNC_BIT) { in vc5_clk_out_prepare()
615 ret = regmap_set_bits(vc5->regmap, in vc5_clk_out_prepare()
616 VC5_RESERVED_X0(hwdata->num), in vc5_clk_out_prepare()
626 ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src); in vc5_clk_out_prepare()
632 ret = regmap_update_bits(vc5->regmap, in vc5_clk_out_prepare()
633 VC5_OUT_DIV_CONTROL(hwdata->num), in vc5_clk_out_prepare()
640 ret = regmap_set_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), in vc5_clk_out_prepare()
645 if (hwdata->clk_output_cfg0_mask) { in vc5_clk_out_prepare()
646 dev_dbg(&vc5->client->dev, "Update output %d mask 0x%0X val 0x%0X\n", in vc5_clk_out_prepare()
647 hwdata->num, hwdata->clk_output_cfg0_mask, in vc5_clk_out_prepare()
648 hwdata->clk_output_cfg0); in vc5_clk_out_prepare()
650 ret = regmap_update_bits(vc5->regmap, in vc5_clk_out_prepare()
651 VC5_CLK_OUTPUT_CFG(hwdata->num, 0), in vc5_clk_out_prepare()
652 hwdata->clk_output_cfg0_mask, in vc5_clk_out_prepare()
653 hwdata->clk_output_cfg0); in vc5_clk_out_prepare()
664 struct vc5_driver_data *vc5 = hwdata->vc5; in vc5_clk_out_unprepare()
667 regmap_clear_bits(vc5->regmap, VC5_CLK_OUTPUT_CFG(hwdata->num, 1), in vc5_clk_out_unprepare()
674 struct vc5_driver_data *vc5 = hwdata->vc5; in vc5_clk_out_get_parent()
685 ret = regmap_read(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), &src); in vc5_clk_out_get_parent()
700 dev_warn(&vc5->client->dev, in vc5_clk_out_get_parent()
708 struct vc5_driver_data *vc5 = hwdata->vc5; in vc5_clk_out_set_parent()
722 return regmap_update_bits(vc5->regmap, VC5_OUT_DIV_CONTROL(hwdata->num), in vc5_clk_out_set_parent()
738 unsigned int idx = clkspec->args[0]; in vc5_of_clk_get()
740 if (idx >= vc5->chip_info->clk_out_cnt) in vc5_of_clk_get()
741 return ERR_PTR(-EINVAL); in vc5_of_clk_get()
743 return &vc5->clk_out[idx].hw; in vc5_of_clk_get()
769 clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_CFG_MASK; in vc5_update_mode()
778 clk_out->clk_output_cfg0 |= in vc5_update_mode()
782 return -EINVAL; in vc5_update_mode()
793 if (!of_property_read_u32(np_output, "idt,voltage-microvolt", in vc5_update_power()
795 clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_PWR_MASK; in vc5_update_power()
798 clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_18; in vc5_update_power()
801 clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_25; in vc5_update_power()
804 clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_PWR_33; in vc5_update_power()
807 return -EINVAL; in vc5_update_power()
813 static int vc5_map_cap_value(u32 femtofarads) in vc5_map_cap_value() argument
818 * The datasheet explicitly states 9000 - 25000 with 0.5pF in vc5_map_cap_value()
824 if (femtofarads < 9000 || femtofarads > 22760) in vc5_map_cap_value()
825 return -EINVAL; in vc5_map_cap_value()
828 * The Programmer's guide shows XTAL[5:0] but in reality, in vc5_map_cap_value()
829 * XTAL[0] and XTAL[1] are both LSB which makes the math in vc5_map_cap_value()
831 * values should be simpler by ignoring XTAL[0] in vc5_map_cap_value()
833 mapped_value = DIV_ROUND_CLOSEST(femtofarads - 9000, 430); in vc5_map_cap_value()
836 * Since the calculation ignores XTAL[0], there is one in vc5_map_cap_value()
854 if (of_property_read_u32(node, "idt,xtal-load-femtofarads", &value)) in vc5_update_cap_load()
866 ret = regmap_update_bits(vc5->regmap, VC5_XTAL_X1_LOAD_CAP, ~0x03, in vc5_update_cap_load()
871 return regmap_update_bits(vc5->regmap, VC5_XTAL_X2_LOAD_CAP, ~0x03, in vc5_update_cap_load()
880 if (!of_property_read_u32(np_output, "idt,slew-percent", &value)) { in vc5_update_slew()
881 clk_out->clk_output_cfg0_mask |= VC5_CLK_OUTPUT_CFG0_SLEW_MASK; in vc5_update_slew()
884 clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_80; in vc5_update_slew()
887 clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_85; in vc5_update_slew()
890 clk_out->clk_output_cfg0 |= VC5_CLK_OUTPUT_CFG0_SLEW_90; in vc5_update_slew()
893 clk_out->clk_output_cfg0 |= in vc5_update_slew()
897 return -EINVAL; in vc5_update_slew()
910 child_name = kasprintf(GFP_KERNEL, "OUT%d", clk_out->num + 1); in vc5_get_output_config()
912 return -ENOMEM; in vc5_get_output_config()
914 np_output = of_get_child_by_name(client->dev.of_node, child_name); in vc5_get_output_config()
931 dev_err(&client->dev, in vc5_get_output_config()
933 clk_out->num + 1); in vc5_get_output_config()
952 vc5 = devm_kzalloc(&client->dev, sizeof(*vc5), GFP_KERNEL); in vc5_probe()
954 return -ENOMEM; in vc5_probe()
957 vc5->client = client; in vc5_probe()
958 vc5->chip_info = i2c_get_match_data(client); in vc5_probe()
960 vc5->pin_xin = devm_clk_get(&client->dev, "xin"); in vc5_probe()
961 if (PTR_ERR(vc5->pin_xin) == -EPROBE_DEFER) in vc5_probe()
962 return -EPROBE_DEFER; in vc5_probe()
964 vc5->pin_clkin = devm_clk_get(&client->dev, "clkin"); in vc5_probe()
965 if (PTR_ERR(vc5->pin_clkin) == -EPROBE_DEFER) in vc5_probe()
966 return -EPROBE_DEFER; in vc5_probe()
968 vc5->regmap = devm_regmap_init_i2c(client, &vc5_regmap_config); in vc5_probe()
969 if (IS_ERR(vc5->regmap)) in vc5_probe()
970 return dev_err_probe(&client->dev, PTR_ERR(vc5->regmap), in vc5_probe()
973 ret = of_property_read_u32(client->dev.of_node, "idt,shutdown", &sd); in vc5_probe()
978 } else if (ret != -EINVAL) { in vc5_probe()
979 return dev_err_probe(&client->dev, ret, in vc5_probe()
983 ret = of_property_read_u32(client->dev.of_node, in vc5_probe()
984 "idt,output-enable-active", &oe); in vc5_probe()
989 } else if (ret != -EINVAL) { in vc5_probe()
990 return dev_err_probe(&client->dev, ret, in vc5_probe()
991 "could not read idt,output-enable-active\n"); in vc5_probe()
994 ret = regmap_update_bits(vc5->regmap, VC5_PRIM_SRC_SHDN, src_mask, in vc5_probe()
1002 if (!IS_ERR(vc5->pin_xin)) { in vc5_probe()
1003 vc5->clk_mux_ins |= VC5_MUX_IN_XIN; in vc5_probe()
1004 parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin); in vc5_probe()
1005 } else if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) { in vc5_probe()
1006 vc5->pin_xin = clk_register_fixed_rate(&client->dev, in vc5_probe()
1007 "internal-xtal", NULL, in vc5_probe()
1009 if (IS_ERR(vc5->pin_xin)) in vc5_probe()
1010 return PTR_ERR(vc5->pin_xin); in vc5_probe()
1011 vc5->clk_mux_ins |= VC5_MUX_IN_XIN; in vc5_probe()
1012 parent_names[init.num_parents++] = __clk_get_name(vc5->pin_xin); in vc5_probe()
1015 if (!IS_ERR(vc5->pin_clkin)) { in vc5_probe()
1016 vc5->clk_mux_ins |= VC5_MUX_IN_CLKIN; in vc5_probe()
1018 __clk_get_name(vc5->pin_clkin); in vc5_probe()
1022 return dev_err_probe(&client->dev, -EINVAL, in vc5_probe()
1025 /* Configure Optional Loading Capacitance for external XTAL */ in vc5_probe()
1026 if (!(vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL)) { in vc5_probe()
1027 ret = vc5_update_cap_load(client->dev.of_node, vc5); in vc5_probe()
1032 init.name = kasprintf(GFP_KERNEL, "%pOFn.mux", client->dev.of_node); in vc5_probe()
1034 ret = -ENOMEM; in vc5_probe()
1041 vc5->clk_mux.init = &init; in vc5_probe()
1042 ret = devm_clk_hw_register(&client->dev, &vc5->clk_mux); in vc5_probe()
1047 if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) { in vc5_probe()
1051 client->dev.of_node); in vc5_probe()
1053 ret = -ENOMEM; in vc5_probe()
1059 parent_names[0] = clk_hw_get_name(&vc5->clk_mux); in vc5_probe()
1061 vc5->clk_mul.init = &init; in vc5_probe()
1062 ret = devm_clk_hw_register(&client->dev, &vc5->clk_mul); in vc5_probe()
1070 init.name = kasprintf(GFP_KERNEL, "%pOFn.pfd", client->dev.of_node); in vc5_probe()
1072 ret = -ENOMEM; in vc5_probe()
1078 if (vc5->chip_info->flags & VC5_HAS_PFD_FREQ_DBL) in vc5_probe()
1079 parent_names[0] = clk_hw_get_name(&vc5->clk_mul); in vc5_probe()
1081 parent_names[0] = clk_hw_get_name(&vc5->clk_mux); in vc5_probe()
1083 vc5->clk_pfd.init = &init; in vc5_probe()
1084 ret = devm_clk_hw_register(&client->dev, &vc5->clk_pfd); in vc5_probe()
1091 init.name = kasprintf(GFP_KERNEL, "%pOFn.pll", client->dev.of_node); in vc5_probe()
1093 ret = -ENOMEM; in vc5_probe()
1099 parent_names[0] = clk_hw_get_name(&vc5->clk_pfd); in vc5_probe()
1101 vc5->clk_pll.num = 0; in vc5_probe()
1102 vc5->clk_pll.vc5 = vc5; in vc5_probe()
1103 vc5->clk_pll.hw.init = &init; in vc5_probe()
1104 ret = devm_clk_hw_register(&client->dev, &vc5->clk_pll.hw); in vc5_probe()
1110 for (n = 0; n < vc5->chip_info->clk_fod_cnt; n++) { in vc5_probe()
1111 idx = vc5_map_index_to_output(vc5->chip_info->model, n); in vc5_probe()
1114 client->dev.of_node, idx); in vc5_probe()
1116 ret = -ENOMEM; in vc5_probe()
1122 parent_names[0] = clk_hw_get_name(&vc5->clk_pll.hw); in vc5_probe()
1124 vc5->clk_fod[n].num = idx; in vc5_probe()
1125 vc5->clk_fod[n].vc5 = vc5; in vc5_probe()
1126 vc5->clk_fod[n].hw.init = &init; in vc5_probe()
1127 ret = devm_clk_hw_register(&client->dev, &vc5->clk_fod[n].hw); in vc5_probe()
1133 /* Register MUX-connected OUT0_I2C_SELB output */ in vc5_probe()
1136 client->dev.of_node); in vc5_probe()
1138 ret = -ENOMEM; in vc5_probe()
1144 parent_names[0] = clk_hw_get_name(&vc5->clk_mux); in vc5_probe()
1146 vc5->clk_out[0].num = idx; in vc5_probe()
1147 vc5->clk_out[0].vc5 = vc5; in vc5_probe()
1148 vc5->clk_out[0].hw.init = &init; in vc5_probe()
1149 ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[0].hw); in vc5_probe()
1154 /* Register FOD-connected OUTx outputs */ in vc5_probe()
1155 for (n = 1; n < vc5->chip_info->clk_out_cnt; n++) { in vc5_probe()
1156 idx = vc5_map_index_to_output(vc5->chip_info->model, n - 1); in vc5_probe()
1157 parent_names[0] = clk_hw_get_name(&vc5->clk_fod[idx].hw); in vc5_probe()
1159 parent_names[1] = clk_hw_get_name(&vc5->clk_mux); in vc5_probe()
1162 clk_hw_get_name(&vc5->clk_out[n - 1].hw); in vc5_probe()
1166 client->dev.of_node, idx + 1); in vc5_probe()
1168 ret = -ENOMEM; in vc5_probe()
1175 vc5->clk_out[n].num = idx; in vc5_probe()
1176 vc5->clk_out[n].vc5 = vc5; in vc5_probe()
1177 vc5->clk_out[n].hw.init = &init; in vc5_probe()
1178 ret = devm_clk_hw_register(&client->dev, &vc5->clk_out[n].hw); in vc5_probe()
1184 ret = vc5_get_output_config(client, &vc5->clk_out[n]); in vc5_probe()
1189 ret = of_clk_add_hw_provider(client->dev.of_node, vc5_of_clk_get, vc5); in vc5_probe()
1191 dev_err_probe(&client->dev, ret, in vc5_probe()
1199 dev_err_probe(&client->dev, ret, in vc5_probe()
1203 if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) in vc5_probe()
1204 clk_unregister_fixed_rate(vc5->pin_xin); in vc5_probe()
1212 of_clk_del_provider(client->dev.of_node); in vc5_remove()
1214 if (vc5->chip_info->flags & VC5_HAS_INTERNAL_XTAL) in vc5_remove()
1215 clk_unregister_fixed_rate(vc5->pin_xin); in vc5_remove()
1222 regcache_cache_only(vc5->regmap, true); in vc5_suspend()
1223 regcache_mark_dirty(vc5->regmap); in vc5_suspend()
1233 regcache_cache_only(vc5->regmap, false); in vc5_resume()
1234 ret = regcache_sync(vc5->regmap); in vc5_resume()