[U-Boot] [PATCH 35/51] drivers: Add ICS8N3QV01 driver

Simon Glass sjg at chromium.org
Wed Jul 19 09:05:54 UTC 2017


On 14 July 2017 at 05:55, Mario Six <mario.six at gdsys.cc> wrote:
> Add a driver for the ICS8N3QV01 Quad-Frequency Programmable VCXO.
>
> Signed-off-by: Mario Six <mario.six at gdsys.cc>
> ---
>
>  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, mario.six at gdsys.cc
> + *
> + * based on the gdsys osd driver, which is
> + *
> + * (C) Copyright 2010
> + * Dirk Eibach,  Guntermann & Drunck GmbH, eibach at gdsys.de
> + *
> + * 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


More information about the U-Boot mailing list