On 14 July 2017 at 05:55, Mario Six <[email protected]> wrote: > Add a driver for the ICS8N3QV01 Quad-Frequency Programmable VCXO. > > Signed-off-by: Mario Six <[email protected]> > --- > > drivers/clk/Kconfig | 6 ++ > drivers/clk/Makefile | 1 + > drivers/clk/ics8n3qv01.c | 184 > +++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 191 insertions(+) > create mode 100644 drivers/clk/ics8n3qv01.c > > diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig > index 44da716b26..f6f3810b64 100644 > --- a/drivers/clk/Kconfig > +++ b/drivers/clk/Kconfig > @@ -56,4 +56,10 @@ source "drivers/clk/uniphier/Kconfig" > source "drivers/clk/exynos/Kconfig" > source "drivers/clk/at91/Kconfig" > > +config ICS8N3QV01 > + bool "Enable ICS8N3QV01 VCXO driver" > + depends on CLK > + help > + Support for the ICS8N3QV01 VCXO.
What is this? Can you describe it a bit more here? > + > endmenu > diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile > index 2746a8016a..d7cc486d23 100644 > --- a/drivers/clk/Makefile > +++ b/drivers/clk/Makefile > @@ -21,3 +21,4 @@ obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o > obj-$(CONFIG_CLK_BOSTON) += clk_boston.o > obj-$(CONFIG_ARCH_ASPEED) += aspeed/ > obj-$(CONFIG_STM32F7) += clk_stm32f7.o > +obj-$(CONFIG_ICS8N3QV01) += ics8n3qv01.o Would be good if we could have these in alpha order. If you have time can you do a patch to tidy that up before or after? > diff --git a/drivers/clk/ics8n3qv01.c b/drivers/clk/ics8n3qv01.c > new file mode 100644 > index 0000000000..f5f4b74982 > --- /dev/null > +++ b/drivers/clk/ics8n3qv01.c > @@ -0,0 +1,184 @@ > +/* > + * (C) Copyright 2017 > + * Mario Six, Guntermann & Drunck GmbH, [email protected] > + * > + * based on the gdsys osd driver, which is > + * > + * (C) Copyright 2010 > + * Dirk Eibach, Guntermann & Drunck GmbH, [email protected] > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +#include <common.h> > +#include <dm.h> > +#include <clk-uclass.h> > +#include <i2c.h> > + > +const long long ICS8N3QV01_FREF = 114285000; > +const long long ICS8N3QV01_FREF_LL = 114285000LL; > +const long long ICS8N3QV01_F_DEFAULT_0 = 156250000LL; > +const long long ICS8N3QV01_F_DEFAULT_1 = 125000000LL; > +const long long ICS8N3QV01_F_DEFAULT_2 = 100000000LL; > +const long long ICS8N3QV01_F_DEFAULT_3 = 25175000LL; > + > +struct ics8n3qv01_priv { > + ulong rate; > +}; > + > +static uint ics8n3qv01_get_fout_calc(struct udevice *dev, uint index) > +{ > + u64 n, mint, mfrac; > + u8 reg_a, reg_b, reg_c, reg_d, reg_f; > + u64 fout_calc; > + > + if (index > 3) What is 3? Should this be an enum/#define? > + return 0; > + > + reg_a = dm_i2c_reg_read(dev, 0 + index); Error checking? > + reg_b = dm_i2c_reg_read(dev, 4 + index); > + reg_c = dm_i2c_reg_read(dev, 8 + index); > + reg_d = dm_i2c_reg_read(dev, 12 + index); > + reg_f = dm_i2c_reg_read(dev, 20 + index); > + > + mint = ((reg_a >> 1) & 0x1f) | (reg_f & 0x20); > + mfrac = ((reg_a & 0x01) << 17) | (reg_b << 9) | (reg_c << 1) > + | (reg_d >> 7); > + n = reg_d & 0x7f; > + > + fout_calc = (mint * ICS8N3QV01_FREF_LL > + + mfrac * ICS8N3QV01_FREF_LL / 262144LL > + + ICS8N3QV01_FREF_LL / 524288LL > + + n / 2) > + / n > + * 1000000 > + / (1000000 - 100); > + > + return fout_calc; > +} > + > +static void ics8n3qv01_calc_parameters(uint fout, uint *_mint, uint *_mfrac, > + uint *_n) > +{ > + uint n, foutiic, fvcoiic, mint; > + u64 mfrac; > + > + n = (2215000000U + fout / 2) / fout; > + if ((n & 1) && (n > 5)) > + n -= 1; > + > + foutiic = fout - (fout / 10000); > + fvcoiic = foutiic * n; > + > + mint = fvcoiic / 114285000; > + if ((mint < 17) || (mint > 63)) > + printf("ics8n3qv01_calc_parameters: cannot determine mint\n"); return error? > + > + mfrac = ((u64)fvcoiic % 114285000LL) * 262144LL > + / 114285000LL; > + > + *_mint = mint; > + *_mfrac = mfrac; > + *_n = n; > +} > + > +static ulong ics8n3qv01_set_rate(struct clk *clk, ulong fout) > +{ > + struct ics8n3qv01_priv *priv = dev_get_priv(clk->dev); > + uint n, mint, mfrac, fout_calc; > + u64 fout_prog; > + long long off_ppm; > + u8 reg0, reg4, reg8, reg12, reg18, reg20; > + > + priv->rate = fout; > + > + fout_calc = ics8n3qv01_get_fout_calc(clk->dev, 1); > + off_ppm = (fout_calc - ICS8N3QV01_F_DEFAULT_1) * 1000000 > + / ICS8N3QV01_F_DEFAULT_1; > + printf("%s: PLL is off by %lld ppm\n", clk->dev->name, off_ppm); debug()? > + fout_prog = (u64)fout * (u64)fout_calc > + / ICS8N3QV01_F_DEFAULT_1; > + ics8n3qv01_calc_parameters(fout_prog, &mint, &mfrac, &n); > + > + reg0 = dm_i2c_reg_read(clk->dev, 0) & 0xc0; > + reg0 |= (mint & 0x1f) << 1; > + reg0 |= (mfrac >> 17) & 0x01; > + dm_i2c_reg_write(clk->dev, 0, reg0); Check error? > + > + reg4 = mfrac >> 9; > + dm_i2c_reg_write(clk->dev, 4, reg4); > + > + reg8 = mfrac >> 1; > + dm_i2c_reg_write(clk->dev, 8, reg8); > + > + reg12 = mfrac << 7; > + reg12 |= n & 0x7f; > + dm_i2c_reg_write(clk->dev, 12, reg12); > + > + reg18 = dm_i2c_reg_read(clk->dev, 18) & 0x03; > + reg18 |= 0x20; > + dm_i2c_reg_write(clk->dev, 18, reg18); > + > + reg20 = dm_i2c_reg_read(clk->dev, 20) & 0x1f; > + reg20 |= mint & (1 << 5); > + dm_i2c_reg_write(clk->dev, 20, reg20); > + > + return 0; > +} > + > +static int ics8n3qv01_request(struct clk *clock) > +{ > + return 0; > +} > + > +static ulong ics8n3qv01_get_rate(struct clk *clk) > +{ > + struct ics8n3qv01_priv *priv = dev_get_priv(clk->dev); > + > + return priv->rate; > +} > + > +static int ics8n3qv01_enable(struct clk *clk) > +{ > + return 0; > +} > + > +static int ics8n3qv01_disable(struct clk *clk) > +{ > + return 0; > +} > + > +static const struct clk_ops ics8n3qv01_ops = { > + .request = ics8n3qv01_request, > + .get_rate = ics8n3qv01_get_rate, > + .set_rate = ics8n3qv01_set_rate, > + .enable = ics8n3qv01_enable, > + .disable = ics8n3qv01_disable, > +}; > + > +static const struct udevice_id ics8n3qv01_ids[] = { > + { .compatible = "idt,ics8n3qv01" }, > + { /* sentinel */ } > +}; > + > +int ics8n3qv01_probe(struct udevice *dev) > +{ > + struct dm_i2c_chip *chip = dev_get_parent_platdata(dev); > + struct udevice *dummy; > + > + if (dm_i2c_probe(dev->parent, chip->chip_addr, chip->flags, &dummy)) { You should not need to probe here - DM should do this for you. > + printf("ics8n3qv01: I2C probe did not succeed.\n"); > + return -1; > + } > + > + return 0; > +} > + > +U_BOOT_DRIVER(ics8n3qv01) = { > + .name = "ics8n3qv01", > + .id = UCLASS_CLK, > + .ops = &ics8n3qv01_ops, > + .of_match = ics8n3qv01_ids, > + .probe = ics8n3qv01_probe, > + .priv_auto_alloc_size = sizeof(struct ics8n3qv01_priv), > +}; > -- > 2.11.0 > Regards, Simon _______________________________________________ U-Boot mailing list [email protected] https://lists.denx.de/listinfo/u-boot

