On Thu, Jan 8, 2015 at 11:01 PM, Mike Looijmans <mike.looijmans@xxxxxxxx> wrote: > Just a ping to inform if you've had had time to look at this? Its in the queue for review this week. A lot to catch up on after the holidays. Thanks for the ping. Regards, Mike > > Mike. > > On 12/04/2014 08:26 AM, Mike Looijmans wrote: >> >> This driver supports the TI CDCE925 programmable clock synthesizer. >> The chip contains two PLLs with spread-spectrum clocking support and >> five output dividers. The driver only supports the following setup, >> and uses a fixed setting for the output muxes: >> Y1 is derived from the input clock >> Y2 and Y3 derive from PLL1 >> Y4 and Y5 derive from PLL2 >> Given a target output frequency, the driver will set the PLL and >> divider to best approximate the desired output. >> >> Signed-off-by: Mike Looijmans <mike.looijmans@xxxxxxxx> >> --- >> >> v2: Coding style check >> Add devicetree binding documentation >> >> .../devicetree/bindings/clock/cdce925.txt | 61 ++ >> drivers/clk/Kconfig | 17 + >> drivers/clk/Makefile | 1 + >> drivers/clk/clk-cdce925.c | 792 >> ++++++++++++++++++++ >> 4 files changed, 871 insertions(+) >> create mode 100644 Documentation/devicetree/bindings/clock/cdce925.txt >> create mode 100644 drivers/clk/clk-cdce925.c >> >> diff --git a/Documentation/devicetree/bindings/clock/cdce925.txt >> b/Documentation/devicetree/bindings/clock/cdce925.txt >> new file mode 100644 >> index 0000000..0eac770 >> --- /dev/null >> +++ b/Documentation/devicetree/bindings/clock/cdce925.txt >> @@ -0,0 +1,61 @@ >> +Binding for TO CDCE925 programmable I2C clock synthesizers. >> + >> +Reference >> +This binding uses the common clock binding[1]. >> + >> +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt >> +[2] http://www.ti.com/product/cdce925 >> + >> +Required properties: >> + - compatible: Shall be one of "cdce925", "cdce925pw", >> + - reg: I2C device address. >> + - clocks: Points to a fixed parent clock that provides the input >> frequency. >> + - #clock-cells: From common clock bindings: Shall be 1. >> + >> +Optional properties: >> + - xtal-load-pf: Crystal load-capacitor value to fine-tune performance on >> a >> + board, or to compensate for external influences. >> + >> + >> +For each connected output Y1 through Y5, a child node should be provided. >> Each >> +child node must have the following properties: >> + - #clock-cells: From common clock bindings: Shall be 0. >> +Optional properties for the output nodes: >> + - clock-frequency: Output frequency to generate. This defines the output >> + frequency set during boot. It can be reprogrammed >> during >> + runtime through the common clock framework. >> + >> +For both PLL1 and PLL2 an optional child node can be used to specify >> spread >> +spectrum clocking parameters. >> + - spread-spectrum: SSC mode as defined in the data sheet. >> + - spread-spectrum-center: Use "centered" mode instead of "max" mode. >> When this >> + is present, the clock runs at the requested frequency on average. >> + >> + >> +Example: >> + >> + clockgen: cdce925pw@64 { >> + compatible = "cdce925"; >> + reg = <0x64>; >> + clocks = <&xtal_27Mhz>; >> + xtal-load-pf = <5>; >> + #clock-cells = <1>; >> + /* PLL options to get SSC 1% centered */ >> + PLL2 { >> + spread-spectrum = <4>; >> + spread-spectrum-center; >> + }; >> + /* Outputs calculate mux and divider settings */ >> + Y1 { >> + #clock-cells = <0>; >> + clock-frequency = <27000>; >> + }; >> + audio_clock: Y2 { >> + #clock-cells = <0>; >> + clock-frequency = <12288000>; /* SPDIF audio */ >> + }; >> + hdmi_pixel_clock: Y4 { >> + #clock-cells = <0>; >> + clock-frequency = <148500000>; /* HD-video */ >> + }; >> + }; >> diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig >> index 455fd17..4e474b3 100644 >> --- a/drivers/clk/Kconfig >> +++ b/drivers/clk/Kconfig >> @@ -77,6 +77,23 @@ config COMMON_CLK_SI570 >> This driver supports Silicon Labs 570/571/598/599 programmable >> clock generators. >> >> +config COMMON_CLK_CDCE925 >> + tristate "Clock driver for TI CDCE925 devices" >> + depends on I2C >> + depends on OF >> + select REGMAP_I2C >> + help >> + ---help--- >> + This driver supports the TI CDCE925 programmable clock >> synthesizer. >> + The chip contains two PLLs with spread-spectrum clocking support >> and >> + five output dividers. The driver only supports the following >> setup, >> + and uses a fixed setting for the output muxes. >> + Y1 is derived from the input clock >> + Y2 and Y3 derive from PLL1 >> + Y4 and Y5 derive from PLL2 >> + Given a target output frequency, the driver will set the PLL and >> + divider to best approximate the desired output. >> + >> config COMMON_CLK_S2MPS11 >> tristate "Clock driver for S2MPS1X/S5M8767 MFD" >> depends on MFD_SEC_CORE >> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile >> index d5fba5b..c476066 100644 >> --- a/drivers/clk/Makefile >> +++ b/drivers/clk/Makefile >> @@ -35,6 +35,7 @@ obj-$(CONFIG_COMMON_CLK_RK808) += >> clk-rk808.o >> obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o >> obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o >> obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o >> +obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o >> obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o >> obj-$(CONFIG_ARCH_U300) += clk-u300.o >> obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o >> diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c >> new file mode 100644 >> index 0000000..faa867f >> --- /dev/null >> +++ b/drivers/clk/clk-cdce925.c >> @@ -0,0 +1,792 @@ >> +/* >> + * Driver for TI Dual PLL CDCE925 clock synthesizer >> + * >> + * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1 >> + * and Y4/Y5 to PLL2. 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-provider.h> >> +#include <linux/clk-private.h> >> +#include <linux/delay.h> >> +#include <linux/module.h> >> +#include <linux/i2c.h> >> +#include <linux/regmap.h> >> +#include <linux/slab.h> >> +#include <linux/gcd.h> >> + >> +/* The chip has 2 PLLs which can be routed through dividers to 5 outputs. >> + * Model this as 2 PLL clocks which are parents to the outputs. >> + */ >> +#define NUMBER_OF_PLLS 2 >> +#define NUMBER_OF_OUTPUTS 5 >> + >> +#define CDCE925_REG_GLOBAL1 0x01 >> +#define CDCE925_REG_Y1SPIPDIVH 0x02 >> +#define CDCE925_REG_PDIVL 0x03 >> +#define CDCE925_REG_XCSEL 0x05 >> +/* PLL parameters start at 0x10, steps of 0x10 */ >> +#define CDCE925_OFFSET_PLL 0x10 >> +/* Add CDCE925_OFFSET_PLL * (pll) to these registers before sending */ >> +#define CDCE925_PLL_MUX_OUTPUTS 0x14 >> +#define CDCE925_PLL_MULDIV 0x18 >> + >> +#define CDCE925_PLL_FREQUENCY_MIN 80000000 >> +#define CDCE925_PLL_FREQUENCY_MAX 230000000 >> +struct clk_cdce925_chip; >> + >> +struct clk_cdce925_output { >> + struct clk_hw hw; >> + struct clk_cdce925_chip *chip; >> + u8 index; >> + u16 pdiv; /* 1..127 for Y2-Y5; 1..1023 for Y1 */ >> +}; >> +#define to_clk_cdce925_output(_hw) \ >> + container_of(_hw, struct clk_cdce925_output, hw) >> + >> +struct clk_cdce925_pll { >> + struct clk_hw hw; >> + struct clk_cdce925_chip *chip; >> + u8 index; >> + u16 m; /* 1..511 */ >> + u16 n; /* 1..4095 */ >> +}; >> +#define to_clk_cdce925_pll(_hw) container_of(_hw, struct >> clk_cdce925_pll, hw) >> + >> +struct clk_cdce925_chip { >> + struct regmap *regmap; >> + struct i2c_client *i2c_client; >> + struct clk_cdce925_pll pll[NUMBER_OF_PLLS]; >> + struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS]; >> +}; >> + >> +/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ >> + >> +static unsigned long cdce925_pll_calculate_rate(unsigned long >> 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, (unsigned long)n, (unsigned long)m); >> +} >> + >> +static unsigned long cdce925_pll_recalc_rate(struct clk_hw *hw, >> + unsigned long parent_rate) >> +{ >> + /* Output frequency of PLL is Fout = (Fin/Pdiv)*(N/M) */ >> + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); >> + >> + return cdce925_pll_calculate_rate(parent_rate, data->n, data->m); >> +} >> + >> +static int cdce925_pll_find_rate(unsigned long rate, >> + unsigned long parent_rate, u16 *n, u16 *m) >> +{ >> + if (rate <= parent_rate) { >> + /* Can always deliver parent_rate in bypass mode */ >> + rate = parent_rate; >> + *n = 0; >> + *m = 0; >> + } else { >> + /* In PLL mode, need to apply min/max range */ >> + unsigned long un; >> + unsigned long um; >> + unsigned long g; >> + >> + if (rate < CDCE925_PLL_FREQUENCY_MIN) >> + rate = CDCE925_PLL_FREQUENCY_MIN; >> + else if (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; >> + } >> + >> + pr_debug("%s(%lu,%lu) n=%u m=%u\n", >> + __func__, rate, parent_rate, *n, *m); >> + >> + return 0; >> +} >> + >> +static long cdce925_pll_round_rate(struct clk_hw *hw, unsigned long rate, >> + unsigned long *parent_rate) >> +{ >> + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); >> + u16 n, m; >> + int ret; >> + >> + pr_debug("%s (index=%d parent_rate=%lu rate=%lu)\n", __func__, >> + data->index, *parent_rate, rate); >> + ret = cdce925_pll_find_rate(rate, *parent_rate, &n, &m); >> + return (long)cdce925_pll_calculate_rate(*parent_rate, n, m); >> +} >> + >> +static int cdce925_pll_set_rate(struct clk_hw *hw, unsigned long rate, >> + unsigned long parent_rate) >> +{ >> + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); >> + >> + if (!rate || (rate == parent_rate)) { >> + data->m = 0; /* Bypass mode */ >> + data->n = 0; >> + return 0; >> + } >> + >> + if ((rate < CDCE925_PLL_FREQUENCY_MIN) || >> + (rate > CDCE925_PLL_FREQUENCY_MAX)) { >> + pr_debug("%s: rate %lu outside PLL range.\n", __func__, >> rate); >> + return -EINVAL; >> + } >> + >> + if (rate < parent_rate) { >> + pr_debug("%s: rate %lu less than parent rate %lu.\n", >> __func__, >> + rate, parent_rate); >> + return -EINVAL; >> + } >> + >> + return cdce925_pll_find_rate(rate, parent_rate, &data->n, >> &data->m); >> +} >> + >> + >> +/* calculate p = max(0, 4 - int(log2 (n/m))) */ >> +static u8 cdce925_pll_calc_p(u16 n, u16 m) >> +{ >> + u8 p; >> + u16 r = n / m; >> + >> + if (r >= 16) >> + return 0; >> + p = 4; >> + while (r > 1) { >> + r >>= 1; >> + --p; >> + } >> + return p; >> +} >> + >> +/* Returns VCO range bits for VCO1_0_RANGE */ >> +static u8 cdce925_pll_calc_range_bits(struct clk_hw *hw, u16 n, u16 m) >> +{ >> + struct clk *parent = clk_get_parent(hw->clk); >> + unsigned long rate = clk_get_rate(parent); >> + >> + rate = mult_frac(rate, (unsigned long)n, (unsigned long)m); >> + if (rate >= 175000000) >> + return 0x3; >> + if (rate >= 150000000) >> + return 0x02; >> + if (rate >= 125000000) >> + return 0x01; >> + return 0x00; >> +} >> + >> +/* I2C clock, hence everything must happen in (un)prepare because this >> + * may sleep */ >> +static int cdce925_pll_prepare(struct clk_hw *hw) >> +{ >> + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); >> + u16 n = data->n; >> + u16 m = data->m; >> + u16 r; >> + u8 q; >> + u8 p; >> + u16 nn; >> + u8 pll[4]; /* Bits are spread out over 4 byte registers */ >> + u8 reg_ofs = data->index * CDCE925_OFFSET_PLL; >> + unsigned i; >> + >> + pr_debug("%s: index=%d\n", __func__, data->index); >> + >> + if ((!m || !n) || (m == n)) { >> + /* Set PLL mux to bypass mode, leave the rest as is */ >> + regmap_update_bits(data->chip->regmap, >> + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80); >> + } else { >> + /* According to data sheet: */ >> + /* p = max(0, 4 - int(log2 (n/m))) */ >> + p = cdce925_pll_calc_p(n, m); >> + /* nn = n * 2^p */ >> + nn = n * BIT(p); >> + /* q = int(nn/m) */ >> + q = nn / m; >> + if ((q < 16) || (1 > 64)) { >> + pr_debug("%s invalid q=%d\n", __func__, q); >> + return -EINVAL; >> + } >> + r = nn - (m*q); >> + if (r > 511) { >> + pr_debug("%s invalid r=%d\n", __func__, r); >> + return -EINVAL; >> + } >> + pr_debug("%s n=%d m=%d p=%d q=%d r=%d\n", __func__, >> + n, m, p, q, r); >> + /* encode into register bits */ >> + pll[0] = n >> 4; >> + pll[1] = ((n & 0x0F) << 4) | ((r >> 5) & 0x0F); >> + pll[2] = ((r & 0x1F) << 3) | ((q >> 3) & 0x07); >> + pll[3] = ((q & 0x07) << 5) | (p << 2) | >> + cdce925_pll_calc_range_bits(hw, n, m); >> + /* Write to registers */ >> + for (i = 0; i < ARRAY_SIZE(pll); ++i) >> + regmap_write(data->chip->regmap, >> + reg_ofs + CDCE925_PLL_MULDIV + i, pll[i]); >> + /* Enable PLL */ >> + regmap_update_bits(data->chip->regmap, >> + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x00); >> + } >> + >> + return 0; >> +} >> + >> +static void cdce925_pll_unprepare(struct clk_hw *hw) >> +{ >> + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); >> + u8 reg_ofs = data->index * CDCE925_OFFSET_PLL; >> + >> + pr_debug("%s: index=%d\n", __func__, data->index); >> + regmap_update_bits(data->chip->regmap, >> + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80); >> +} >> + >> +static const struct clk_ops cdce925_pll_ops = { >> + .prepare = cdce925_pll_prepare, >> + .unprepare = cdce925_pll_unprepare, >> + .recalc_rate = cdce925_pll_recalc_rate, >> + .round_rate = cdce925_pll_round_rate, >> + .set_rate = cdce925_pll_set_rate, >> +}; >> + >> + >> +static void cdce925_clk_set_pdiv(struct clk_cdce925_output *data, u16 >> pdiv) >> +{ >> + pr_debug("%s: index=%d pdiv=%d\n", __func__, data->index, pdiv); >> + switch (data->index) { >> + case 0: >> + regmap_update_bits(data->chip->regmap, >> + CDCE925_REG_Y1SPIPDIVH, >> + 0x03, (pdiv >> 8) & 0x03); >> + regmap_write(data->chip->regmap, 0x03, pdiv & 0xFF); >> + break; >> + case 1: >> + regmap_update_bits(data->chip->regmap, 0x16, 0x7F, pdiv); >> + break; >> + case 2: >> + regmap_update_bits(data->chip->regmap, 0x17, 0x7F, pdiv); >> + break; >> + case 3: >> + regmap_update_bits(data->chip->regmap, 0x26, 0x7F, pdiv); >> + break; >> + case 4: >> + regmap_update_bits(data->chip->regmap, 0x27, 0x7F, pdiv); >> + break; >> + } >> +} >> + >> +static void cdce925_clk_activate(struct clk_cdce925_output *data) >> +{ >> + pr_debug("%s: index=%d\n", __func__, data->index); >> + switch (data->index) { >> + case 0: >> + regmap_update_bits(data->chip->regmap, >> + CDCE925_REG_Y1SPIPDIVH, 0x0c, 0x0c); >> + break; >> + case 1: >> + case 2: >> + regmap_update_bits(data->chip->regmap, 0x14, 0x03, 0x03); >> + break; >> + case 3: >> + case 4: >> + regmap_update_bits(data->chip->regmap, 0x24, 0x03, 0x03); >> + break; >> + } >> +} >> + >> +static int cdce925_clk_prepare(struct clk_hw *hw) >> +{ >> + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); >> + >> + pr_debug("%s: index=%d\n", __func__, data->index); >> + cdce925_clk_set_pdiv(data, data->pdiv); >> + cdce925_clk_activate(data); >> + return 0; >> +} >> + >> +static void cdce925_clk_unprepare(struct clk_hw *hw) >> +{ >> + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); >> + >> + pr_debug("%s: index=%d\n", __func__, data->index); >> + /* Disable clock by setting divider to "0" */ >> + cdce925_clk_set_pdiv(data, 0); >> +} >> + >> +static unsigned long cdce925_clk_recalc_rate(struct clk_hw *hw, >> + unsigned long parent_rate) >> +{ >> + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); >> + >> + pr_debug("%s: index=%d parent_rate=%lu pdiv=%d\n", __func__, >> + data->index, parent_rate, data->pdiv); >> + if (data->pdiv) >> + return parent_rate / data->pdiv; >> + return 0; >> +} >> + >> +static u16 cdce925_calc_divider(unsigned long rate, >> + unsigned long parent_rate) >> +{ >> + if (rate >= parent_rate) { >> + return 1; >> + } else if (rate) { >> + unsigned long divider = DIV_ROUND_CLOSEST(parent_rate, >> rate); >> + >> + if (divider > 0x7F) /* TODO: Y1 has 10-bit divider */ >> + divider = 0x7F; >> + return (u16)divider; >> + } else { >> + return 0; >> + } >> +} >> + >> +static unsigned long cdce925_clk_best_parent_rate( >> + struct clk_hw *hw, unsigned long rate) >> +{ >> + struct clk *pll = clk_get_parent(hw->clk); >> + struct clk *root = clk_get_parent(pll); >> + unsigned long root_rate = clk_get_rate(root); >> + unsigned long best_rate_error = rate; >> + u16 pdiv_min; >> + u16 pdiv_max; >> + u16 pdiv_best; >> + u16 pdiv_now; >> + >> + if (root_rate % rate == 0) >> + return root_rate; /* Don't need the PLL, use bypass */ >> + >> + pdiv_min = (u16)max(1u, DIV_ROUND_UP(CDCE925_PLL_FREQUENCY_MIN, >> rate)); >> + pdiv_max = (u16)min(127u, CDCE925_PLL_FREQUENCY_MAX / rate); >> + >> + if (pdiv_min > pdiv_max) >> + return 0; /* No can do? */ >> + >> + pdiv_best = pdiv_min; >> + for (pdiv_now = pdiv_min; pdiv_now < pdiv_max; ++pdiv_now) { >> + unsigned long target_rate = rate * pdiv_now; >> + long pll_rate = clk_round_rate(pll, target_rate); >> + unsigned long actual_rate; >> + unsigned long rate_error; >> + >> + 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 */ >> + } >> + >> + return rate * pdiv_best; >> +} >> + >> +static long cdce925_clk_round_rate(struct clk_hw *hw, unsigned long rate, >> + unsigned long *parent_rate) >> +{ >> + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); >> + unsigned long l_parent_rate = *parent_rate; >> + u16 divider = cdce925_calc_divider(rate, l_parent_rate); >> + >> + pr_debug("%s (index=%d parent_rate=%lu rate=%lu)\n", __func__, >> + data->index, l_parent_rate, rate); >> + if (l_parent_rate / divider != rate) { >> + l_parent_rate = cdce925_clk_best_parent_rate(hw, rate); >> + divider = cdce925_calc_divider(rate, l_parent_rate); >> + *parent_rate = l_parent_rate; >> + } >> + pr_debug("%s parent_rate=%lu pdiv=%u\n", __func__, >> + l_parent_rate, divider); >> + if (divider) >> + return (long)(l_parent_rate / divider); >> + return 0; >> +} >> + >> +static int cdce925_clk_set_rate(struct clk_hw *hw, unsigned long rate, >> + unsigned long parent_rate) >> +{ >> + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); >> + >> + data->pdiv = cdce925_calc_divider(rate, parent_rate); >> + pr_debug("%s (index=%d parent_rate=%lu rate=%lu div=%d)\n", >> __func__, >> + data->index, parent_rate, rate, data->pdiv); >> + return 0; >> +} >> + >> +static const struct clk_ops cdce925_clk_ops = { >> + .prepare = cdce925_clk_prepare, >> + .unprepare = cdce925_clk_unprepare, >> + .recalc_rate = cdce925_clk_recalc_rate, >> + .round_rate = cdce925_clk_round_rate, >> + .set_rate = cdce925_clk_set_rate, >> +}; >> + >> + >> +static u16 cdce925_y1_calc_divider(unsigned long rate, >> + unsigned long parent_rate) >> +{ >> + if (rate >= parent_rate) >> + return 1; >> + else if (rate) { >> + unsigned long divider = DIV_ROUND_CLOSEST(parent_rate, >> rate); >> + >> + if (divider > 0x3FF) /* Y1 has 10-bit divider */ >> + divider = 0x3FF; >> + return (u16)divider; >> + } else >> + return 0; >> +} >> + >> +static long cdce925_clk_y1_round_rate(struct clk_hw *hw, unsigned long >> rate, >> + unsigned long *parent_rate) >> +{ >> + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); >> + unsigned long l_parent_rate = *parent_rate; >> + u16 divider = cdce925_y1_calc_divider(rate, l_parent_rate); >> + >> + pr_debug("%s (index=%d parent_rate=%lu rate=%lu pdiv=%u)\n", >> __func__, >> + data->index, l_parent_rate, rate, divider); >> + if (divider) >> + return (long)(l_parent_rate / divider); >> + return 0; >> +} >> + >> +static int cdce925_clk_y1_set_rate(struct clk_hw *hw, unsigned long rate, >> + unsigned long parent_rate) >> +{ >> + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); >> + >> + data->pdiv = cdce925_y1_calc_divider(rate, parent_rate); >> + pr_debug("%s (index=%d parent_rate=%lu rate=%lu div=%d)\n", >> __func__, >> + data->index, parent_rate, rate, data->pdiv); >> + return 0; >> +} >> + >> +static const struct clk_ops cdce925_clk_y1_ops = { >> + .prepare = cdce925_clk_prepare, >> + .unprepare = cdce925_clk_unprepare, >> + .recalc_rate = cdce925_clk_recalc_rate, >> + .round_rate = cdce925_clk_y1_round_rate, >> + .set_rate = cdce925_clk_y1_set_rate, >> +}; >> + >> + >> +static struct regmap_config cdce925_regmap_config = { >> + .name = "configuration0", >> + .reg_bits = 8, >> + .val_bits = 8, >> + .cache_type = REGCACHE_RBTREE, >> + .max_register = 0x2F, >> +}; >> + >> +#define CDCE925_I2C_COMMAND_BLOCK_TRANSFER 0x00 >> +#define CDCE925_I2C_COMMAND_BYTE_TRANSFER 0x80 >> + >> +static int cdce925_regmap_i2c_write( >> + void *context, const void *data, size_t count) >> +{ >> + struct device *dev = context; >> + struct i2c_client *i2c = to_i2c_client(dev); >> + int ret; >> + u8 reg_data[2]; >> + >> + if (count != 2) >> + return -ENOTSUPP; >> + >> + /* First byte is command code */ >> + reg_data[0] = CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 *)data)[0]; >> + reg_data[1] = ((u8 *)data)[1]; >> + >> + dev_dbg(&i2c->dev, "%s(%u) %#x %#x\n", __func__, count, >> + reg_data[0], reg_data[1]); >> + >> + ret = i2c_master_send(i2c, reg_data, count); >> + if (likely(ret == count)) >> + return 0; >> + else if (ret < 0) >> + return ret; >> + else >> + return -EIO; >> +} >> + >> +static int cdce925_regmap_i2c_read(void *context, >> + const void *reg, size_t reg_size, void *val, size_t val_size) >> +{ >> + struct device *dev = context; >> + struct i2c_client *i2c = to_i2c_client(dev); >> + struct i2c_msg xfer[2]; >> + int ret; >> + u8 reg_data[2]; >> + >> + if (reg_size != 1) >> + return -ENOTSUPP; >> + >> + xfer[0].addr = i2c->addr; >> + xfer[0].flags = 0; >> + xfer[0].buf = reg_data; >> + if (val_size == 1) { >> + reg_data[0] = >> + CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 >> *)reg)[0]; >> + xfer[0].len = 1; >> + } else { >> + reg_data[0] = >> + CDCE925_I2C_COMMAND_BLOCK_TRANSFER | ((u8 >> *)reg)[0]; >> + reg_data[1] = val_size; >> + xfer[0].len = 2; >> + } >> + >> + xfer[1].addr = i2c->addr; >> + xfer[1].flags = I2C_M_RD; >> + xfer[1].len = val_size; >> + xfer[1].buf = val; >> + >> + ret = i2c_transfer(i2c->adapter, xfer, 2); >> + if (likely(ret == 2)) { >> + dev_dbg(&i2c->dev, "%s(%u, %u) %#x %#x\n", __func__, >> + reg_size, val_size, reg_data[0], *((u8 >> *)val)); >> + return 0; >> + } else if (ret < 0) >> + return ret; >> + else >> + return -EIO; >> +} >> + >> +/* The CDCE925 uses a funky way to read/write registers. Bulk mode is >> + * just weird, so just use the single byte mode exclusively. */ >> +static struct regmap_bus regmap_cdce925_bus = { >> + .write = cdce925_regmap_i2c_write, >> + .read = cdce925_regmap_i2c_read, >> +}; >> + >> +static int cdce925_probe(struct i2c_client *client, >> + const struct i2c_device_id *id) >> +{ >> + struct clk_cdce925_chip *data; >> + struct device_node *node = client->dev.of_node; >> + const char *parent_name; >> + struct clk_init_data init; >> + struct clk *clk; >> + u32 value; >> + int i; >> + int err; >> + struct device_node *np_output; >> + char child_name[6]; >> + >> + dev_dbg(&client->dev, "%s\n", __func__); >> + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); >> + if (!data) >> + return -ENOMEM; >> + >> + data->i2c_client = client; >> + data->regmap = devm_regmap_init(&client->dev, ®map_cdce925_bus, >> + &client->dev, &cdce925_regmap_config); >> + if (IS_ERR(data->regmap)) { >> + dev_err(&client->dev, "failed to allocate register >> map\n"); >> + return PTR_ERR(data->regmap); >> + } >> + i2c_set_clientdata(client, data); >> + >> + parent_name = of_clk_get_parent_name(node, 0); >> + if (!parent_name) { >> + dev_err(&client->dev, "missing parent clock\n"); >> + return -ENODEV; >> + } >> + dev_dbg(&client->dev, "parent is: %s\n", parent_name); >> + >> + if (of_property_read_u32(node, "xtal-load-pf", &value) == 0) >> + regmap_write(data->regmap, >> + CDCE925_REG_XCSEL, (value << 3) & 0xF8); >> + /* PWDN bit */ >> + regmap_update_bits(data->regmap, CDCE925_REG_GLOBAL1, BIT(4), 0); >> + >> + /* Set input source for Y1 to be the XTAL */ >> + regmap_update_bits(data->regmap, 0x02, BIT(7), 0); >> + >> + init.ops = &cdce925_pll_ops; >> + init.flags = 0; >> + init.parent_names = &parent_name; >> + init.num_parents = parent_name ? 1 : 0; >> + >> + /* Register PLL clocks */ >> + for (i = 0; i < NUMBER_OF_PLLS; ++i) { >> + init.name = kasprintf(GFP_KERNEL, "%s.pll%d", >> + client->dev.of_node->name, i); >> + data->pll[i].chip = data; >> + data->pll[i].hw.init = &init; >> + data->pll[i].index = i; >> + clk = devm_clk_register(&client->dev, &data->pll[i].hw); >> + kfree(init.name); /* clock framework made a copy of the >> name */ >> + if (IS_ERR(clk)) { >> + dev_err(&client->dev, "Failed register PLL %d\n", >> i); >> + err = PTR_ERR(clk); >> + 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(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); >> + } >> + } >> + >> + /* 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, "%s.Y1", >> client->dev.of_node->name); >> + data->clk[0].chip = data; >> + data->clk[0].hw.init = &init; >> + data->clk[0].index = 0; >> + data->clk[0].pdiv = 1; >> + clk = devm_clk_register(&client->dev, &data->clk[0].hw); >> + kfree(init.name); /* clock framework made a copy of the name */ >> + if (IS_ERR(clk)) { >> + dev_err(&client->dev, "clock registration Y1 failed\n"); >> + err = PTR_ERR(clk); >> + 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 < NUMBER_OF_OUTPUTS; ++i) { >> + init.name = kasprintf(GFP_KERNEL, "%s.Y%d", >> + client->dev.of_node->name, i+1); >> + 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 = &data->pll[0].hw.clk->name; >> + break; >> + case 3: >> + case 4: >> + /* Mux Y4/5 to PLL2 */ >> + init.parent_names = &data->pll[1].hw.clk->name; >> + break; >> + } >> + clk = devm_clk_register(&client->dev, &data->clk[i].hw); >> + kfree(init.name); /* clock framework made a copy of the >> name */ >> + if (IS_ERR(clk)) { >> + dev_err(&client->dev, "clock registration >> failed\n"); >> + err = PTR_ERR(clk); >> + goto error; >> + } >> + } >> + >> + /* Fetch settings from devicetree, if any */ >> + for (i = 0; i < NUMBER_OF_OUTPUTS; ++i) { >> + sprintf(child_name, "Y%d", i+1); >> + np_output = of_get_child_by_name(node, child_name); >> + if (!np_output) { >> + /* Disable unlisted/unused clock outputs >> explicitly */ >> + cdce925_clk_unprepare(&data->clk[i].hw); >> + continue; >> + } >> + clk = data->clk[i].hw.clk; >> + if (!of_property_read_u32(np_output, >> + "clock-frequency", &value)) { >> + err = clk_set_rate(clk, value); >> + if (err) >> + dev_err(&client->dev, >> + "unable to set frequency %ud\n", >> + value); >> + } >> + if (of_property_read_bool(np_output, "clock-enabled")) { >> + err = clk_prepare_enable(clk); >> + if (err) >> + dev_err(&client->dev, >> + "Failed to enable clock %s\n", >> + init.name); >> + } else { >> + cdce925_clk_unprepare(&data->clk[i].hw); >> + } >> + err = of_clk_add_provider(np_output, >> + of_clk_src_simple_get, clk); >> + if (err) >> + dev_err(&client->dev, >> + "unable to add clock provider '%s'\n", >> + init.name); >> + } >> + >> + return 0; >> + >> +error: >> + return err; >> +} >> + >> +static const struct i2c_device_id cdce925_id[] = { >> + { "cdce925", 0 }, >> + { } >> +}; >> +MODULE_DEVICE_TABLE(i2c, cdce925_id); >> + >> +static const struct of_device_id clk_cdce925_of_match[] = { >> + { .compatible = "cdce925pw" }, >> + { .compatible = "cdce925" }, >> + { }, >> +}; >> +MODULE_DEVICE_TABLE(of, clk_cdce925_of_match); >> + >> +static struct i2c_driver cdce925_driver = { >> + .driver = { >> + .name = "cdce925", >> + .of_match_table = of_match_ptr(clk_cdce925_of_match), >> + }, >> + .probe = cdce925_probe, >> + .id_table = cdce925_id, >> +}; >> +module_i2c_driver(cdce925_driver); >> + >> +MODULE_AUTHOR("Mike Looijmans <mike.looijmans@xxxxxxxx>"); >> +MODULE_DESCRIPTION("cdce925 driver"); >> +MODULE_LICENSE("GPL"); >> > > > > Met vriendelijke groet / kind regards, > > Mike Looijmans > System Expert > > > TOPIC Embedded Systems > Eindhovenseweg 32-C, NL-5683 KH Best > Postbus 440, NL-5680 AK Best > Telefoon: (+31) (0) 499 33 69 79 > Telefax: (+31) (0) 499 33 69 70 > E-mail: mike.looijmans@xxxxxxxx > Website: www.topic.nl > > Please consider the environment before printing this e-mail > > Topic zoekt gedreven (embedded) software specialisten! > http://topic.nl/vacatures/topic-zoekt-software-engineers/ > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html