/* * clk-si5351.c: Silicon Laboratories Si5351A/B/C I2C Clock Generator * * Sebastian Hesselbarth * Rabeeh Khoury * * References: * [1] "Si5351A/B/C Data Sheet" * http://www.silabs.com/Support%20Documents/TechnicalDocs/Si5351.pdf * [2] "Manually Generating an Si5351 Register Map" * http://www.silabs.com/Support%20Documents/TechnicalDocs/AN619.pdf * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "clk-si5351.h" struct si5351_driver_data; struct si5351_parameters { unsigned long p1; unsigned long p2; unsigned long p3; int valid; }; struct si5351_hw_data { struct clk_hw hw; struct si5351_driver_data *drvdata; struct si5351_parameters params; unsigned char num; }; struct si5351_driver_data { enum si5351_variant variant; struct i2c_client *client; struct regmap *regmap; struct clk_onecell_data onecell; struct clk *pxtal; const char *pxtal_name; struct clk_hw xtal; struct clk *pclkin; const char *pclkin_name; struct clk_hw clkin; struct si5351_hw_data pll[2]; struct si5351_hw_data *msynth; struct si5351_hw_data *clkout; }; static const char const *si5351_input_names[] = { "xtal", "clkin" }; static const char const *si5351_pll_names[] = { "plla", "pllb", "vxco" }; static const char const *si5351_msynth_names[] = { "ms0", "ms1", "ms2", "ms3", "ms4", "ms5", "ms6", "ms7" }; static const char const *si5351_clkout_names[] = { "clk0", "clk1", "clk2", "clk3", "clk4", "clk5", "clk6", "clk7" }; /* * Si5351 i2c regmap */ static inline u8 si5351_reg_read(struct si5351_driver_data *drvdata, u8 reg) { u32 val; int ret; ret = regmap_read(drvdata->regmap, reg, &val); if (ret) { dev_err(&drvdata->client->dev, "unable to read from reg%02x\n", reg); return 0; } return (u8)val; } static inline int si5351_bulk_read(struct si5351_driver_data *drvdata, u8 reg, u8 count, u8 *buf) { return regmap_bulk_read(drvdata->regmap, reg, buf, count); } static inline int si5351_reg_write(struct si5351_driver_data *drvdata, u8 reg, u8 val) { return regmap_write(drvdata->regmap, reg, val); } static inline int si5351_bulk_write(struct si5351_driver_data *drvdata, u8 reg, u8 count, const u8 *buf) { return regmap_raw_write(drvdata->regmap, reg, buf, count); } static inline int si5351_set_bits(struct si5351_driver_data *drvdata, u8 reg, u8 mask, u8 val) { return regmap_update_bits(drvdata->regmap, reg, mask, val); } static inline u8 si5351_msynth_params_address(int num) { if (num > 5) return SI5351_CLK6_PARAMETERS + (num - 6); return SI5351_CLK0_PARAMETERS + (SI5351_PARAMETERS_LENGTH * num); } static void si5351_read_parameters(struct si5351_driver_data *drvdata, u8 reg, struct si5351_parameters *params) { u8 buf[SI5351_PARAMETERS_LENGTH]; switch (reg) { case SI5351_CLK6_PARAMETERS: case SI5351_CLK7_PARAMETERS: buf[0] = si5351_reg_read(drvdata, reg); params->p1 = buf[0]; params->p2 = 0; params->p3 = 1; break; default: si5351_bulk_read(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf); params->p1 = ((buf[2] & 0x03) << 16) | (buf[3] << 8) | buf[4]; params->p2 = ((buf[5] & 0x0f) << 16) | (buf[6] << 8) | buf[7]; params->p3 = ((buf[5] & 0xf0) << 12) | (buf[0] << 8) | buf[1]; } params->valid = 1; } static void si5351_write_parameters(struct si5351_driver_data *drvdata, u8 reg, struct si5351_parameters *params) { u8 buf[SI5351_PARAMETERS_LENGTH]; switch (reg) { case SI5351_CLK6_PARAMETERS: case SI5351_CLK7_PARAMETERS: buf[0] = params->p1 & 0xff; si5351_reg_write(drvdata, reg, buf[0]); break; default: buf[0] = ((params->p3 & 0x0ff00) >> 8) & 0xff; buf[1] = params->p3 & 0xff; /* save rdiv and divby4 */ buf[2] = si5351_reg_read(drvdata, reg + 2) & ~0x03; buf[2] |= ((params->p1 & 0x30000) >> 16) & 0x03; buf[3] = ((params->p1 & 0x0ff00) >> 8) & 0xff; buf[4] = params->p1 & 0xff; buf[5] = ((params->p3 & 0xf0000) >> 12) | ((params->p2 & 0xf0000) >> 16); buf[6] = ((params->p2 & 0x0ff00) >> 8) & 0xff; buf[7] = params->p2 & 0xff; si5351_bulk_write(drvdata, reg, SI5351_PARAMETERS_LENGTH, buf); } } static bool si5351_regmap_is_volatile(struct device *dev, unsigned int reg) { switch (reg) { case SI5351_DEVICE_STATUS: case SI5351_INTERRUPT_STATUS: case SI5351_PLL_RESET: return true; } return false; } static bool si5351_regmap_is_writeable(struct device *dev, unsigned int reg) { /* reserved registers */ if (reg >= 4 && reg <= 8) return false; if (reg >= 10 && reg <= 14) return false; if (reg >= 173 && reg <= 176) return false; if (reg >= 178 && reg <= 182) return false; /* read-only */ if (reg == SI5351_DEVICE_STATUS) return false; return true; } static struct regmap_config si5351_regmap_config = { .reg_bits = 8, .val_bits = 8, .cache_type = REGCACHE_RBTREE, .max_register = 187, .writeable_reg = si5351_regmap_is_writeable, .volatile_reg = si5351_regmap_is_volatile, }; /* * Si5351 xtal clock input */ static int si5351_xtal_prepare(struct clk_hw *hw) { struct si5351_driver_data *drvdata = container_of(hw, struct si5351_driver_data, xtal); si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, SI5351_XTAL_ENABLE, SI5351_XTAL_ENABLE); return 0; } static void si5351_xtal_unprepare(struct clk_hw *hw) { struct si5351_driver_data *drvdata = container_of(hw, struct si5351_driver_data, xtal); si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, SI5351_XTAL_ENABLE, 0); } static const struct clk_ops si5351_xtal_ops = { .prepare = si5351_xtal_prepare, .unprepare = si5351_xtal_unprepare, }; /* * Si5351 clkin clock input (Si5351C only) */ static int si5351_clkin_prepare(struct clk_hw *hw) { struct si5351_driver_data *drvdata = container_of(hw, struct si5351_driver_data, clkin); si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, SI5351_CLKIN_ENABLE, SI5351_CLKIN_ENABLE); return 0; } static void si5351_clkin_unprepare(struct clk_hw *hw) { struct si5351_driver_data *drvdata = container_of(hw, struct si5351_driver_data, clkin); si5351_set_bits(drvdata, SI5351_FANOUT_ENABLE, SI5351_CLKIN_ENABLE, 0); } /* * CMOS clock source constraints: * The input frequency range of the PLL is 10Mhz to 40MHz. * If CLKIN is >40MHz, the input divider must be used. */ static unsigned long si5351_clkin_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct si5351_driver_data *drvdata = container_of(hw, struct si5351_driver_data, clkin); unsigned long rate; unsigned char idiv; rate = parent_rate; if (parent_rate > 160000000) { idiv = SI5351_CLKIN_DIV_8; rate /= 8; } else if (parent_rate > 80000000) { idiv = SI5351_CLKIN_DIV_4; rate /= 4; } else if (parent_rate > 40000000) { idiv = SI5351_CLKIN_DIV_2; rate /= 2; } else { idiv = SI5351_CLKIN_DIV_1; } si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, SI5351_CLKIN_DIV_MASK, idiv); dev_dbg(&drvdata->client->dev, "%s - clkin div = %d, rate = %lu\n", __func__, (1 << (idiv >> 6)), rate); return rate; } static const struct clk_ops si5351_clkin_ops = { .prepare = si5351_clkin_prepare, .unprepare = si5351_clkin_unprepare, .recalc_rate = si5351_clkin_recalc_rate, }; /* * Si5351 vxco clock input (Si5351B only) */ static int si5351_vxco_prepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); dev_warn(&hwdata->drvdata->client->dev, "VXCO currently unsupported\n"); return 0; } static void si5351_vxco_unprepare(struct clk_hw *hw) { } static unsigned long si5351_vxco_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { return 0; } static int si5351_vxco_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent) { return 0; } static const struct clk_ops si5351_vxco_ops = { .prepare = si5351_vxco_prepare, .unprepare = si5351_vxco_unprepare, .recalc_rate = si5351_vxco_recalc_rate, .set_rate = si5351_vxco_set_rate, }; /* * Si5351 pll a/b * * Feedback Multisynth Divider Equations [2] * * fVCO = fIN * (a + b/c) * * with 15 + 0/1048575 <= (a + b/c) <= 90 + 0/1048575 and * fIN = fXTAL or fIN = fCLKIN/CLKIN_DIV * * Feedback Multisynth Register Equations * * (1) MSNx_P1[17:0] = 128 * a + floor(128 * b/c) - 512 * (2) MSNx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c * (3) MSNx_P3[19:0] = c * * Transposing (2) yields: (4) floor(128 * b/c) = (128 * b / MSNx_P2)/c * * Using (4) on (1) yields: * MSNx_P1 = 128 * a + (128 * b/MSNx_P2)/c - 512 * MSNx_P1 + 512 + MSNx_P2/c = 128 * a + 128 * b/c * * a + b/c = (MSNx_P1 + MSNx_P2/MSNx_P3 + 512)/128 * = (MSNx_P1*MSNx_P3 + MSNx_P2 + 512*MSNx_P3)/(128*MSNx_P3) * */ static int _si5351_pll_reparent(struct si5351_driver_data *drvdata, int num, enum si5351_pll_src parent) { u8 mask = (num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE; if (parent == SI5351_PLL_SRC_DEFAULT) return 0; if (num > 2) return -EINVAL; if (drvdata->variant != SI5351_VARIANT_C && parent != SI5351_PLL_SRC_XTAL) return -EINVAL; si5351_set_bits(drvdata, SI5351_PLL_INPUT_SOURCE, mask, (parent == SI5351_PLL_SRC_XTAL) ? 0 : mask); return 0; } static unsigned char si5351_pll_get_parent(struct clk_hw *hw) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); u8 mask = (hwdata->num == 0) ? SI5351_PLLA_SOURCE : SI5351_PLLB_SOURCE; u8 val; val = si5351_reg_read(hwdata->drvdata, SI5351_PLL_INPUT_SOURCE); return (val & mask) ? 1 : 0; } static int si5351_pll_set_parent(struct clk_hw *hw, u8 index) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); if (hwdata->drvdata->variant != SI5351_VARIANT_C && index > 0) return -EPERM; if (index > 1) return -EINVAL; return _si5351_pll_reparent(hwdata->drvdata, hwdata->num, (index == 0) ? SI5351_PLL_SRC_XTAL : SI5351_PLL_SRC_CLKIN); } static unsigned long si5351_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS : SI5351_PLLB_PARAMETERS; unsigned long long rate; if (!hwdata->params.valid) si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params); if (hwdata->params.p3 == 0) return parent_rate; /* fVCO = fIN * (P1*P3 + 512*P3 + P2)/(128*P3) */ rate = hwdata->params.p1 * hwdata->params.p3; rate += 512 * hwdata->params.p3; rate += hwdata->params.p2; rate *= parent_rate; do_div(rate, 128 * hwdata->params.p3); 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), hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, parent_rate, (unsigned long)rate); return (unsigned long)rate; } static long si5351_pll_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); unsigned long rfrac, denom, a, b, c; unsigned long long lltmp; if (rate < SI5351_PLL_VCO_MIN) rate = SI5351_PLL_VCO_MIN; if (rate > SI5351_PLL_VCO_MAX) rate = SI5351_PLL_VCO_MAX; /* determine integer part of feedback equation */ a = rate / *parent_rate; if (a < SI5351_PLL_A_MIN) rate = *parent_rate * SI5351_PLL_A_MIN; if (a > SI5351_PLL_A_MAX) rate = *parent_rate * SI5351_PLL_A_MAX; /* find best approximation for b/c = fVCO mod fIN */ denom = 1000 * 1000; lltmp = rate % (*parent_rate); lltmp *= denom; do_div(lltmp, *parent_rate); rfrac = (unsigned long)lltmp; b = 0; c = 1; if (rfrac) rational_best_approximation(rfrac, denom, SI5351_PLL_B_MAX, SI5351_PLL_C_MAX, &b, &c); /* calculate parameters */ hwdata->params.p3 = c; hwdata->params.p2 = (128 * b) % c; hwdata->params.p1 = 128 * a; hwdata->params.p1 += (128 * b / c); hwdata->params.p1 -= 512; /* recalculate rate by fIN * (a + b/c) */ lltmp = *parent_rate; lltmp *= b; do_div(lltmp, c); rate = (unsigned long)lltmp; rate += *parent_rate * a; 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, *parent_rate, rate); return rate; } static int si5351_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); u8 reg = (hwdata->num == 0) ? SI5351_PLLA_PARAMETERS : SI5351_PLLB_PARAMETERS; /* write multisynth parameters */ si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params); /* plla/pllb ctrl is in clk6/clk7 ctrl registers */ si5351_set_bits(hwdata->drvdata, SI5351_CLK6_CTRL + hwdata->num, SI5351_CLK_INTEGER_MODE, (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0); 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), hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, parent_rate, rate); return 0; } static const struct clk_ops si5351_pll_ops = { .set_parent = si5351_pll_set_parent, .get_parent = si5351_pll_get_parent, .recalc_rate = si5351_pll_recalc_rate, .round_rate = si5351_pll_round_rate, .set_rate = si5351_pll_set_rate, }; /* * Si5351 multisync divider * * for fOUT <= 150 MHz: * * fOUT = (fIN * (a + b/c)) / CLKOUTDIV * * with 6 + 0/1048575 <= (a + b/c) <= 1800 + 0/1048575 and * fIN = fVCO0, fVCO1 * * Output Clock Multisynth Register Equations * * MSx_P1[17:0] = 128 * a + floor(128 * b/c) - 512 * 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 * * for 150MHz < fOUT <= 160MHz: * * MSx_P1 = 0, MSx_P2 = 0, MSx_P3 = 1, MSx_INT = 1, MSx_DIVBY4 = 11b */ static int _si5351_msynth_reparent(struct si5351_driver_data *drvdata, int num, enum si5351_multisynth_src parent) { if (parent == SI5351_MULTISYNTH_SRC_DEFAULT) return 0; if (num > 8) return -EINVAL; si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_PLL_SELECT, (parent == SI5351_MULTISYNTH_SRC_VCO0) ? 0 : SI5351_CLK_PLL_SELECT); return 0; } static unsigned char si5351_msynth_get_parent(struct clk_hw *hw) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); u8 val; val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num); return (val & SI5351_CLK_PLL_SELECT) ? 1 : 0; } static int si5351_msynth_set_parent(struct clk_hw *hw, u8 index) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); return _si5351_msynth_reparent(hwdata->drvdata, hwdata->num, (index == 0) ? SI5351_MULTISYNTH_SRC_VCO0 : SI5351_MULTISYNTH_SRC_VCO1); } static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); u8 reg = si5351_msynth_params_address(hwdata->num); unsigned long long rate; unsigned long m; 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 */ rate = parent_rate; if (hwdata->num > 5) { m = hwdata->params.p1; } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) & SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) { m = 4; } else { rate *= 128 * hwdata->params.p3; m = hwdata->params.p1 * hwdata->params.p3; m += hwdata->params.p2; m += 512 * hwdata->params.p3; } if (m == 0) return 0; do_div(rate, m); 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), hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, m, parent_rate, (unsigned long)rate); return (unsigned long)rate; } static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); unsigned long long lltmp; unsigned long a, b, c; int divby4; /* multisync6-7 can only handle freqencies < 150MHz */ if (hwdata->num >= 6 && rate > SI5351_MULTISYNTH67_MAX_FREQ) rate = SI5351_MULTISYNTH67_MAX_FREQ; /* multisync frequency is 1MHz .. 160MHz */ if (rate > SI5351_MULTISYNTH_MAX_FREQ) rate = SI5351_MULTISYNTH_MAX_FREQ; if (rate < SI5351_MULTISYNTH_MIN_FREQ) rate = SI5351_MULTISYNTH_MIN_FREQ; divby4 = 0; if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ) divby4 = 1; /* multisync can set pll */ if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) { /* * find largest integer divider for max * vco frequency and given target rate */ if (divby4 == 0) { lltmp = SI5351_PLL_VCO_MAX; do_div(lltmp, rate); a = (unsigned long)lltmp; } else a = 4; b = 0; c = 1; *parent_rate = a * rate; } else { unsigned long rfrac, denom; /* disable divby4 */ if (divby4) { rate = SI5351_MULTISYNTH_DIVBY4_FREQ; divby4 = 0; } /* determine integer part of divider equation */ 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) a = SI5351_MULTISYNTH_A_MAX; /* find best approximation for b/c = fVCO mod fOUT */ denom = 1000 * 1000; lltmp = (*parent_rate) % rate; lltmp *= denom; do_div(lltmp, rate); rfrac = (unsigned long)lltmp; b = 0; c = 1; if (rfrac) rational_best_approximation(rfrac, denom, SI5351_MULTISYNTH_B_MAX, SI5351_MULTISYNTH_C_MAX, &b, &c); } /* recalculate rate by fOUT = fIN / (a + b/c) */ lltmp = *parent_rate; lltmp *= c; do_div(lltmp, a * c + b); rate = (unsigned long)lltmp; /* calculate parameters */ if (divby4) { hwdata->params.p3 = 1; hwdata->params.p2 = 0; hwdata->params.p1 = 0; } else { hwdata->params.p3 = c; hwdata->params.p2 = (128 * b) % c; hwdata->params.p1 = 128 * a; hwdata->params.p1 += (128 * b / c); hwdata->params.p1 -= 512; } 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, *parent_rate, rate); return rate; } static int si5351_msynth_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); u8 reg = si5351_msynth_params_address(hwdata->num); int divby4 = 0; /* write multisynth parameters */ si5351_write_parameters(hwdata->drvdata, reg, &hwdata->params); if (rate > SI5351_MULTISYNTH_DIVBY4_FREQ) divby4 = 1; /* enable/disable integer mode and divby4 on multisynth0-5 */ if (hwdata->num < 6) { si5351_set_bits(hwdata->drvdata, reg + 2, SI5351_OUTPUT_CLK_DIVBY4, (divby4) ? SI5351_OUTPUT_CLK_DIVBY4 : 0); si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, SI5351_CLK_INTEGER_MODE, (hwdata->params.p2 == 0) ? SI5351_CLK_INTEGER_MODE : 0); } 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), hwdata->params.p1, hwdata->params.p2, hwdata->params.p3, divby4, parent_rate, rate); return 0; } static const struct clk_ops si5351_msynth_ops = { .set_parent = si5351_msynth_set_parent, .get_parent = si5351_msynth_get_parent, .recalc_rate = si5351_msynth_recalc_rate, .round_rate = si5351_msynth_round_rate, .set_rate = si5351_msynth_set_rate, }; /* * Si5351 clkout divider */ static int _si5351_clkout_reparent(struct si5351_driver_data *drvdata, int num, enum si5351_clkout_src parent) { u8 val; if (num > 8) return -EINVAL; switch (parent) { case SI5351_CLKOUT_SRC_MSYNTH_N: val = SI5351_CLK_INPUT_MULTISYNTH_N; break; case SI5351_CLKOUT_SRC_MSYNTH_0_4: /* clk0/clk4 can only connect to its own multisync */ if (num == 0 || num == 4) val = SI5351_CLK_INPUT_MULTISYNTH_N; else val = SI5351_CLK_INPUT_MULTISYNTH_0_4; break; case SI5351_CLKOUT_SRC_XTAL: val = SI5351_CLK_INPUT_XTAL; break; case SI5351_CLKOUT_SRC_CLKIN: if (drvdata->variant != SI5351_VARIANT_C) return -EINVAL; val = SI5351_CLK_INPUT_CLKIN; break; default: return 0; } si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_INPUT_MASK, val); return 0; } static int _si5351_clkout_set_drive_strength( struct si5351_driver_data *drvdata, int num, enum si5351_drive_strength drive) { u8 mask; if (num > 8) return -EINVAL; switch (drive) { case SI5351_DRIVE_2MA: mask = SI5351_CLK_DRIVE_STRENGTH_2MA; break; case SI5351_DRIVE_4MA: mask = SI5351_CLK_DRIVE_STRENGTH_4MA; break; case SI5351_DRIVE_6MA: mask = SI5351_CLK_DRIVE_STRENGTH_6MA; break; case SI5351_DRIVE_8MA: mask = SI5351_CLK_DRIVE_STRENGTH_8MA; break; default: return 0; } si5351_set_bits(drvdata, SI5351_CLK0_CTRL + num, SI5351_CLK_DRIVE_STRENGTH_MASK, mask); return 0; } static int si5351_clkout_prepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, SI5351_CLK_POWERDOWN, 0); si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, (1 << hwdata->num), 0); return 0; } static void si5351_clkout_unprepare(struct clk_hw *hw) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, SI5351_CLK_POWERDOWN, SI5351_CLK_POWERDOWN); si5351_set_bits(hwdata->drvdata, SI5351_OUTPUT_ENABLE_CTRL, (1 << hwdata->num), (1 << hwdata->num)); } static u8 si5351_clkout_get_parent(struct clk_hw *hw) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); int index = 0; unsigned char val; val = si5351_reg_read(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num); switch (val & SI5351_CLK_INPUT_MASK) { case SI5351_CLK_INPUT_MULTISYNTH_N: index = 0; break; case SI5351_CLK_INPUT_MULTISYNTH_0_4: index = 1; break; case SI5351_CLK_INPUT_XTAL: index = 2; break; case SI5351_CLK_INPUT_CLKIN: index = 3; break; } return index; } static int si5351_clkout_set_parent(struct clk_hw *hw, u8 index) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); enum si5351_clkout_src parent = SI5351_CLKOUT_SRC_DEFAULT; switch (index) { case 0: parent = SI5351_CLKOUT_SRC_MSYNTH_N; break; case 1: parent = SI5351_CLKOUT_SRC_MSYNTH_0_4; break; case 2: parent = SI5351_CLKOUT_SRC_XTAL; break; case 3: parent = SI5351_CLKOUT_SRC_CLKIN; break; } return _si5351_clkout_reparent(hwdata->drvdata, hwdata->num, parent); } static unsigned long si5351_clkout_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); unsigned char reg; unsigned char rdiv; if (hwdata->num <= 5) reg = si5351_msynth_params_address(hwdata->num) + 2; else reg = SI5351_CLK6_7_OUTPUT_DIVIDER; rdiv = si5351_reg_read(hwdata->drvdata, reg); if (hwdata->num == 6) { rdiv &= SI5351_OUTPUT_CLK6_DIV_MASK; } else { rdiv &= SI5351_OUTPUT_CLK_DIV_MASK; rdiv >>= SI5351_OUTPUT_CLK_DIV_SHIFT; } return parent_rate >> rdiv; } static long si5351_clkout_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); unsigned char rdiv; /* clkout6/7 can only handle output freqencies < 150MHz */ if (hwdata->num >= 6 && rate > SI5351_CLKOUT67_MAX_FREQ) rate = SI5351_CLKOUT67_MAX_FREQ; /* clkout freqency is 8kHz - 160MHz */ if (rate > SI5351_CLKOUT_MAX_FREQ) rate = SI5351_CLKOUT_MAX_FREQ; if (rate < SI5351_CLKOUT_MIN_FREQ) rate = SI5351_CLKOUT_MIN_FREQ; /* request frequency if multisync master */ if (__clk_get_flags(hwdata->hw.clk) & CLK_SET_RATE_PARENT) { /* use r divider for frequencies below 1MHz */ rdiv = SI5351_OUTPUT_CLK_DIV_1; while (rate < SI5351_MULTISYNTH_MIN_FREQ && rdiv < SI5351_OUTPUT_CLK_DIV_128) { rdiv += 1; rate *= 2; } *parent_rate = rate; } else { unsigned long new_rate, new_err, err; /* round to closed rdiv */ rdiv = SI5351_OUTPUT_CLK_DIV_1; new_rate = *parent_rate; err = abs(new_rate - rate); do { new_rate >>= 1; new_err = abs(new_rate - rate); if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128) break; rdiv++; err = new_err; } while (1); } rate = *parent_rate >> rdiv; 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), *parent_rate, rate); return rate; } static int si5351_clkout_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct si5351_hw_data *hwdata = container_of(hw, struct si5351_hw_data, hw); unsigned long new_rate, new_err, err; unsigned char rdiv; /* round to closed rdiv */ rdiv = SI5351_OUTPUT_CLK_DIV_1; new_rate = parent_rate; err = abs(new_rate - rate); do { new_rate >>= 1; new_err = abs(new_rate - rate); if (new_err > err || rdiv == SI5351_OUTPUT_CLK_DIV_128) break; rdiv++; err = new_err; } while (1); /* write output divider */ switch (hwdata->num) { case 6: si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER, SI5351_OUTPUT_CLK6_DIV_MASK, rdiv); break; case 7: si5351_set_bits(hwdata->drvdata, SI5351_CLK6_7_OUTPUT_DIVIDER, SI5351_OUTPUT_CLK_DIV_MASK, rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT); break; default: si5351_set_bits(hwdata->drvdata, si5351_msynth_params_address(hwdata->num) + 2, SI5351_OUTPUT_CLK_DIV_MASK, rdiv << SI5351_OUTPUT_CLK_DIV_SHIFT); } /* powerup clkout */ si5351_set_bits(hwdata->drvdata, SI5351_CLK0_CTRL + hwdata->num, SI5351_CLK_POWERDOWN, 0); 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), parent_rate, rate); return 0; } static const struct clk_ops si5351_clkout_ops = { .prepare = si5351_clkout_prepare, .unprepare = si5351_clkout_unprepare, .set_parent = si5351_clkout_set_parent, .get_parent = si5351_clkout_get_parent, .recalc_rate = si5351_clkout_recalc_rate, .round_rate = si5351_clkout_round_rate, .set_rate = si5351_clkout_set_rate, }; /* * Si5351 i2c probe and DT */ #ifdef CONFIG_OF static const struct of_device_id si5351_dt_ids[] = { { .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A, }, { .compatible = "silabs,si5351a-msop", .data = (void *)SI5351_VARIANT_A3, }, { .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B, }, { .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C, }, { } }; MODULE_DEVICE_TABLE(of, si5351_dt_ids); static int si5351_dt_parse(struct i2c_client *client) { 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; u32 val; 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 */ of_property_for_each_u32(np, "silabs,pll-source", prop, p, num) { if (num >= 2) { dev_err(&client->dev, "invalid pll %d on pll-source prop\n", num); return -EINVAL; } p = of_prop_next_u32(prop, p, &val); if (!p) { dev_err(&client->dev, "missing pll-source for pll %d\n", num); return -EINVAL; } switch (val) { case 0: pdata->pll_src[num] = SI5351_PLL_SRC_XTAL; break; case 1: if (pdata->variant != SI5351_VARIANT_C) { dev_err(&client->dev, "invalid parent %d for pll %d\n", val, num); return -EINVAL; } pdata->pll_src[num] = SI5351_PLL_SRC_CLKIN; break; default: dev_err(&client->dev, "invalid parent %d for pll %d\n", val, num); return -EINVAL; } } /* per clkout properties */ for_each_child_of_node(np, child) { if (of_property_read_u32(child, "reg", &num)) { dev_err(&client->dev, "missing reg property of %s\n", child->name); return -EINVAL; } if (num >= 8 || (pdata->variant == SI5351_VARIANT_A3 && num >= 3)) { dev_err(&client->dev, "invalid clkout %d\n", num); return -EINVAL; } if (!of_property_read_u32(child, "silabs,multisynth-source", &val)) { switch (val) { case 0: pdata->clkout[num].multisynth_src = SI5351_MULTISYNTH_SRC_VCO0; break; case 1: pdata->clkout[num].multisynth_src = SI5351_MULTISYNTH_SRC_VCO1; break; default: dev_err(&client->dev, "invalid parent %d for multisynth %d\n", val, num); return -EINVAL; } } if (!of_property_read_u32(child, "silabs,clock-source", &val)) { switch (val) { case 0: pdata->clkout[num].clkout_src = SI5351_CLKOUT_SRC_MSYNTH_N; break; case 1: pdata->clkout[num].clkout_src = SI5351_CLKOUT_SRC_MSYNTH_0_4; break; case 2: pdata->clkout[num].clkout_src = SI5351_CLKOUT_SRC_XTAL; break; case 3: if (pdata->variant != SI5351_VARIANT_C) { dev_err(&client->dev, "invalid parent %d for clkout %d\n", val, num); return -EINVAL; } pdata->clkout[num].clkout_src = SI5351_CLKOUT_SRC_CLKIN; break; default: dev_err(&client->dev, "invalid parent %d for clkout %d\n", val, num); return -EINVAL; } } if (!of_property_read_u32(child, "silabs,drive-strength", &val)) { switch (val) { case SI5351_DRIVE_2MA: case SI5351_DRIVE_4MA: case SI5351_DRIVE_6MA: case SI5351_DRIVE_8MA: pdata->clkout[num].drive = val; break; default: dev_err(&client->dev, "invalid drive strength %d for clkout %d\n", val, num); return -EINVAL; } } if (!of_property_read_u32(child, "clock-frequency", &val)) pdata->clkout[num].rate = val; pdata->clkout[num].pll_master = of_property_read_bool(child, "silabs,pll-master"); } client->dev.platform_data = pdata; return 0; } #else static int si5351_dt_parse(struct i2c_client *client) { return 0; } #endif /* CONFIG_OF */ static int si5351_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct si5351_platform_data *pdata; struct si5351_driver_data *drvdata; struct clk_init_data init; struct clk *clk; const char *parent_names[4]; u8 num_parents, num_clocks; int ret, n; ret = si5351_dt_parse(client); if (ret) return ret; pdata = client->dev.platform_data; if (!pdata) return -EINVAL; drvdata = devm_kzalloc(&client->dev, sizeof(*drvdata), GFP_KERNEL); if (drvdata == NULL) { dev_err(&client->dev, "unable to allocate driver data\n"); return -ENOMEM; } i2c_set_clientdata(client, drvdata); drvdata->client = client; drvdata->variant = pdata->variant; drvdata->pxtal = pdata->clk_xtal; drvdata->pclkin = pdata->clk_clkin; drvdata->regmap = devm_regmap_init_i2c(client, &si5351_regmap_config); if (IS_ERR(drvdata->regmap)) { dev_err(&client->dev, "failed to allocate register map\n"); return PTR_ERR(drvdata->regmap); } /* 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, SI5351_PLLA_SOURCE | SI5351_PLLB_SOURCE, 0); /* setup clock configuration */ for (n = 0; n < 2; n++) { ret = _si5351_pll_reparent(drvdata, n, pdata->pll_src[n]); if (ret) { dev_err(&client->dev, "failed to reparent pll %d to %d\n", n, pdata->pll_src[n]); return ret; } } for (n = 0; n < 8; n++) { ret = _si5351_msynth_reparent(drvdata, n, pdata->clkout[n].multisynth_src); if (ret) { dev_err(&client->dev, "failed to reparent multisynth %d to %d\n", n, pdata->clkout[n].multisynth_src); return ret; } ret = _si5351_clkout_reparent(drvdata, n, pdata->clkout[n].clkout_src); if (ret) { dev_err(&client->dev, "failed to reparent clkout %d to %d\n", n, pdata->clkout[n].clkout_src); return ret; } ret = _si5351_clkout_set_drive_strength(drvdata, n, pdata->clkout[n].drive); if (ret) { dev_err(&client->dev, "failed set drive strength of clkout%d to %d\n", n, pdata->clkout[n].drive); return ret; } } /* register xtal input clock gate */ memset(&init, 0, sizeof(init)); init.name = si5351_input_names[0]; init.ops = &si5351_xtal_ops; init.flags = 0; if (!IS_ERR(drvdata->pxtal)) { drvdata->pxtal_name = __clk_get_name(drvdata->pxtal); init.parent_names = &drvdata->pxtal_name; init.num_parents = 1; } drvdata->xtal.init = &init; 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); } /* register clkin input clock gate */ if (drvdata->variant == SI5351_VARIANT_C) { memset(&init, 0, sizeof(init)); init.name = si5351_input_names[1]; init.ops = &si5351_clkin_ops; if (!IS_ERR(drvdata->pclkin)) { drvdata->pclkin_name = __clk_get_name(drvdata->pclkin); init.parent_names = &drvdata->pclkin_name; init.num_parents = 1; } drvdata->clkin.init = &init; clk = devm_clk_register(&client->dev, &drvdata->clkin); if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); return PTR_ERR(clk); } } /* Si5351C allows to mux either xtal or clkin to PLL input */ num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 2 : 1; parent_names[0] = si5351_input_names[0]; parent_names[1] = si5351_input_names[1]; /* register PLLA */ drvdata->pll[0].num = 0; drvdata->pll[0].drvdata = drvdata; drvdata->pll[0].hw.init = &init; memset(&init, 0, sizeof(init)); init.name = si5351_pll_names[0]; init.ops = &si5351_pll_ops; init.flags = 0; init.parent_names = parent_names; init.num_parents = num_parents; 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; } /* register PLLB or VXCO (Si5351B) */ drvdata->pll[1].num = 1; drvdata->pll[1].drvdata = drvdata; drvdata->pll[1].hw.init = &init; memset(&init, 0, sizeof(init)); if (drvdata->variant == SI5351_VARIANT_B) { init.name = si5351_pll_names[2]; init.ops = &si5351_vxco_ops; init.flags = CLK_IS_ROOT; init.parent_names = NULL; init.num_parents = 0; } else { init.name = si5351_pll_names[1]; init.ops = &si5351_pll_ops; init.flags = 0; init.parent_names = parent_names; init.num_parents = num_parents; } 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; } /* register clk multisync and clk out divider */ num_clocks = (drvdata->variant == SI5351_VARIANT_A3) ? 3 : 8; parent_names[0] = si5351_pll_names[0]; if (drvdata->variant == SI5351_VARIANT_B) parent_names[1] = si5351_pll_names[2]; else parent_names[1] = si5351_pll_names[1]; drvdata->msynth = devm_kzalloc(&client->dev, num_clocks * sizeof(*drvdata->msynth), GFP_KERNEL); drvdata->clkout = devm_kzalloc(&client->dev, num_clocks * sizeof(*drvdata->clkout), GFP_KERNEL); drvdata->onecell.clk_num = num_clocks; drvdata->onecell.clks = devm_kzalloc(&client->dev, num_clocks * sizeof(*drvdata->onecell.clks), GFP_KERNEL); if (WARN_ON(!drvdata->msynth || !drvdata->clkout || !drvdata->onecell.clks)) return -ENOMEM; for (n = 0; n < num_clocks; n++) { drvdata->msynth[n].num = n; drvdata->msynth[n].drvdata = drvdata; drvdata->msynth[n].hw.init = &init; memset(&init, 0, sizeof(init)); init.name = si5351_msynth_names[n]; init.ops = &si5351_msynth_ops; init.flags = 0; if (pdata->clkout[n].pll_master) init.flags |= CLK_SET_RATE_PARENT; init.parent_names = parent_names; init.num_parents = 2; clk = devm_clk_register(&client->dev, &drvdata->msynth[n].hw); if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); return -EINVAL; } } num_parents = (drvdata->variant == SI5351_VARIANT_C) ? 4 : 3; parent_names[2] = si5351_input_names[0]; parent_names[3] = si5351_input_names[1]; for (n = 0; n < num_clocks; n++) { parent_names[0] = si5351_msynth_names[n]; parent_names[1] = (n < 4) ? si5351_msynth_names[0] : si5351_msynth_names[4]; drvdata->clkout[n].num = n; drvdata->clkout[n].drvdata = drvdata; drvdata->clkout[n].hw.init = &init; memset(&init, 0, sizeof(init)); init.name = si5351_clkout_names[n]; init.ops = &si5351_clkout_ops; init.flags = 0; if (pdata->clkout[n].clkout_src == SI5351_CLKOUT_SRC_MSYNTH_N) init.flags |= CLK_SET_RATE_PARENT; init.parent_names = parent_names; init.num_parents = num_parents; clk = devm_clk_register(&client->dev, &drvdata->clkout[n].hw); if (IS_ERR(clk)) { dev_err(&client->dev, "unable to register %s\n", init.name); return -EINVAL; } drvdata->onecell.clks[n] = clk; /* set initial clkout rate */ if (pdata->clkout[n].rate != 0) { int ret; ret = clk_set_rate(clk, pdata->clkout[n].rate); if (ret != 0) { dev_err(&client->dev, "Cannot set rate : %d\n", ret); } } } ret = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, &drvdata->onecell); if (ret) { dev_err(&client->dev, "unable to add clk provider\n"); return ret; } return 0; } static const struct i2c_device_id si5351_i2c_ids[] = { { "silabs,si5351", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, si5351_i2c_ids); static struct i2c_driver si5351_driver = { .driver = { .name = "si5351", .of_match_table = of_match_ptr(si5351_dt_ids), }, .probe = si5351_i2c_probe, .id_table = si5351_i2c_ids, }; module_i2c_driver(si5351_driver); MODULE_AUTHOR("Sebastian Hesselbarth