root/drivers/clk/clk-eyeq.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * PLL clock driver for the Mobileye EyeQ5, EyeQ6L and EyeQ6H platforms.
 *
 * This controller handles:
 *  - Read-only PLLs, all derived from the same main crystal clock.
 *  - It also exposes divider clocks, those are children to PLLs.
 *  - Fixed factor clocks, children to PLLs.
 *
 * Parent clock is expected to be constant. This driver's registers live in a
 * shared region called OLB. Some PLLs and fixed-factors are initialised early
 * by of_clk_init(); if so, two clk providers are registered.
 *
 * We use eqc_ as prefix, as-in "EyeQ Clock", but way shorter.
 *
 * Copyright (C) 2024 Mobileye Vision Technologies Ltd.
 */

/*
 * Set pr_fmt() for printing from eqc_early_init().
 * It is called at of_clk_init() stage (read: really early).
 */
#define pr_fmt(fmt) "clk-eyeq: " fmt

#include <linux/array_size.h>
#include <linux/auxiliary_bus.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/io.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/overflow.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>

#include <dt-bindings/clock/mobileye,eyeq5-clk.h>

/* In frac mode, it enables fractional noise canceling DAC. Else, no function. */
#define PCSR0_DAC_EN                    BIT(0)
/* Fractional or integer mode */
#define PCSR0_DSM_EN                    BIT(1)
#define PCSR0_PLL_EN                    BIT(2)
/* All clocks output held at 0 */
#define PCSR0_FOUTPOSTDIV_EN            BIT(3)
#define PCSR0_POST_DIV1                 GENMASK(6, 4)
#define PCSR0_POST_DIV2                 GENMASK(9, 7)
#define PCSR0_REF_DIV                   GENMASK(15, 10)
#define PCSR0_INTIN                     GENMASK(27, 16)
#define PCSR0_BYPASS                    BIT(28)
/* Bits 30..29 are reserved */
#define PCSR0_PLL_LOCKED                BIT(31)

#define PCSR1_RESET                     BIT(0)
#define PCSR1_SSGC_DIV                  GENMASK(4, 1)
/* Spread amplitude (% = 0.1 * SPREAD[4:0]) */
#define PCSR1_SPREAD                    GENMASK(9, 5)
#define PCSR1_DIS_SSCG                  BIT(10)
/* Down-spread or center-spread */
#define PCSR1_DOWN_SPREAD               BIT(11)
#define PCSR1_FRAC_IN                   GENMASK(31, 12)

struct eqc_pll {
        unsigned int    index;
        const char      *name;
        unsigned int    reg64;
};

/*
 * Divider clock. Divider is 2*(v+1), with v the register value.
 * Min divider is 2, max is 2*(2^width).
 */
struct eqc_div {
        unsigned int    index;
        const char      *name;
        unsigned int    parent;
        unsigned int    reg;
        u8              shift;
        u8              width;
};

struct eqc_fixed_factor {
        unsigned int    index;
        const char      *name;
        unsigned int    mult;
        unsigned int    div;
        unsigned int    parent;
};

struct eqc_match_data {
        unsigned int            pll_count;
        const struct eqc_pll    *plls;

        unsigned int            div_count;
        const struct eqc_div    *divs;

        unsigned int                    fixed_factor_count;
        const struct eqc_fixed_factor   *fixed_factors;

        const char              *reset_auxdev_name;
        const char              *pinctrl_auxdev_name;

        unsigned int            early_clk_count;
};

struct eqc_early_match_data {
        unsigned int            early_pll_count;
        const struct eqc_pll    *early_plls;

        unsigned int                    early_fixed_factor_count;
        const struct eqc_fixed_factor   *early_fixed_factors;

        /*
         * We want our of_xlate callback to EPROBE_DEFER instead of dev_err()
         * and EINVAL. For that, we must know the total clock count.
         */
        unsigned int            late_clk_count;
};

/*
 * Both factors (mult and div) must fit in 32 bits. When an operation overflows,
 * this function throws away low bits so that factors still fit in 32 bits.
 *
 * Precision loss depends on amplitude of mult and div. Worst theoretical
 * loss is: (UINT_MAX+1) / UINT_MAX - 1 = 2.3e-10.
 * This is 1Hz every 4.3GHz.
 */
static void eqc_pll_downshift_factors(unsigned long *mult, unsigned long *div)
{
        unsigned long biggest;
        unsigned int shift;

        /* This function can be removed if mult/div switch to unsigned long. */
        static_assert(sizeof_field(struct clk_fixed_factor, mult) == sizeof(unsigned int));
        static_assert(sizeof_field(struct clk_fixed_factor, div) == sizeof(unsigned int));

        /* No overflow, nothing to be done. */
        if (*mult <= UINT_MAX && *div <= UINT_MAX)
                return;

        /*
         * Compute the shift required to bring the biggest factor into unsigned
         * int range. That is, shift its highest set bit to the unsigned int
         * most significant bit.
         */
        biggest = max(*mult, *div);
        shift = __fls(biggest) - (BITS_PER_BYTE * sizeof(unsigned int)) + 1;

        *mult >>= shift;
        *div >>= shift;
}

static int eqc_pll_parse_registers(u32 r0, u32 r1, unsigned long *mult,
                                   unsigned long *div, unsigned long *acc)
{
        u32 spread;

        if (r0 & PCSR0_BYPASS) {
                *mult = 1;
                *div = 1;
                *acc = 0;
                return 0;
        }

        if (!(r0 & PCSR0_PLL_LOCKED))
                return -EINVAL;

        *mult = FIELD_GET(PCSR0_INTIN, r0);
        *div = FIELD_GET(PCSR0_REF_DIV, r0);
        if (r0 & PCSR0_FOUTPOSTDIV_EN)
                *div *= FIELD_GET(PCSR0_POST_DIV1, r0) * FIELD_GET(PCSR0_POST_DIV2, r0);

        /* Fractional mode, in 2^20 (0x100000) parts. */
        if (r0 & PCSR0_DSM_EN) {
                *div *= (1ULL << 20);
                *mult = *mult * (1ULL << 20) + FIELD_GET(PCSR1_FRAC_IN, r1);
        }

        if (!*mult || !*div)
                return -EINVAL;

        if (r1 & (PCSR1_RESET | PCSR1_DIS_SSCG)) {
                *acc = 0;
                return 0;
        }

        /*
         * Spread spectrum.
         *
         * Spread is 1/1000 parts of frequency, accuracy is half of
         * that. To get accuracy, convert to ppb (parts per billion).
         *
         * acc = spread * 1e6 / 2
         *   with acc in parts per billion and,
         *        spread in parts per thousand.
         */
        spread = FIELD_GET(PCSR1_SPREAD, r1);
        *acc = spread * 500000;

        if (r1 & PCSR1_DOWN_SPREAD) {
                /*
                 * Downspreading: the central frequency is half a
                 * spread lower.
                 */
                *mult *= 2000 - spread;
                *div *= 2000;

                /*
                 * Previous operation might overflow 32 bits. If it
                 * does, throw away the least amount of low bits.
                 */
                eqc_pll_downshift_factors(mult, div);
        }

        return 0;
}

static void eqc_probe_init_plls(struct device *dev, const struct eqc_match_data *data,
                                void __iomem *base, struct clk_hw_onecell_data *cells)
{
        unsigned long mult, div, acc;
        const struct eqc_pll *pll;
        struct clk_hw *hw;
        unsigned int i;
        u32 r0, r1;
        u64 val;
        int ret;

        for (i = 0; i < data->pll_count; i++) {
                pll = &data->plls[i];

                val = readq(base + pll->reg64);
                r0 = val;
                r1 = val >> 32;

                ret = eqc_pll_parse_registers(r0, r1, &mult, &div, &acc);
                if (ret) {
                        dev_warn(dev, "failed parsing state of %s\n", pll->name);
                        cells->hws[pll->index] = ERR_PTR(ret);
                        continue;
                }

                hw = clk_hw_register_fixed_factor_with_accuracy_fwname(dev,
                                dev->of_node, pll->name, "ref", 0, mult, div, acc);
                cells->hws[pll->index] = hw;
                if (IS_ERR(hw))
                        dev_warn(dev, "failed registering %s: %pe\n", pll->name, hw);
        }
}

static void eqc_probe_init_divs(struct device *dev, const struct eqc_match_data *data,
                                void __iomem *base, struct clk_hw_onecell_data *cells)
{
        struct clk_parent_data parent_data = { };
        const struct eqc_div *div;
        struct clk_hw *parent;
        void __iomem *reg;
        struct clk_hw *hw;
        unsigned int i;

        for (i = 0; i < data->div_count; i++) {
                div = &data->divs[i];
                reg = base + div->reg;
                parent = cells->hws[div->parent];

                if (IS_ERR(parent)) {
                        /* Parent is in early clk provider. */
                        parent_data.index = div->parent;
                        parent_data.hw = NULL;
                } else {
                        /* Avoid clock lookup when we already have the hw reference. */
                        parent_data.index = 0;
                        parent_data.hw = parent;
                }

                hw = clk_hw_register_divider_table_parent_data(dev, div->name,
                                &parent_data, 0, reg, div->shift, div->width,
                                CLK_DIVIDER_EVEN_INTEGERS, NULL, NULL);
                cells->hws[div->index] = hw;
                if (IS_ERR(hw))
                        dev_warn(dev, "failed registering %s: %pe\n",
                                 div->name, hw);
        }
}

static void eqc_probe_init_fixed_factors(struct device *dev,
                                         const struct eqc_match_data *data,
                                         struct clk_hw_onecell_data *cells)
{
        const struct eqc_fixed_factor *ff;
        struct clk_hw *hw, *parent_hw;
        unsigned int i;

        for (i = 0; i < data->fixed_factor_count; i++) {
                ff = &data->fixed_factors[i];
                parent_hw = cells->hws[ff->parent];

                if (IS_ERR(parent_hw)) {
                        /* Parent is in early clk provider. */
                        hw = clk_hw_register_fixed_factor_index(dev, ff->name,
                                        ff->parent, 0, ff->mult, ff->div);
                } else {
                        /* Avoid clock lookup when we already have the hw reference. */
                        hw = clk_hw_register_fixed_factor_parent_hw(dev, ff->name,
                                        parent_hw, 0, ff->mult, ff->div);
                }

                cells->hws[ff->index] = hw;
                if (IS_ERR(hw))
                        dev_warn(dev, "failed registering %s: %pe\n",
                                 ff->name, hw);
        }
}

static void eqc_auxdev_release(struct device *dev)
{
        struct auxiliary_device *adev = to_auxiliary_dev(dev);

        kfree(adev);
}

static int eqc_auxdev_create(struct device *dev, void __iomem *base,
                             const char *name, u32 id)
{
        struct auxiliary_device *adev;
        int ret;

        adev = kzalloc_obj(*adev);
        if (!adev)
                return -ENOMEM;

        adev->name = name;
        adev->dev.parent = dev;
        adev->dev.platform_data = (void __force *)base;
        adev->dev.release = eqc_auxdev_release;
        adev->id = id;

        ret = auxiliary_device_init(adev);
        if (ret)
                return ret;

        ret = auxiliary_device_add(adev);
        if (ret)
                auxiliary_device_uninit(adev);

        return ret;
}

static int eqc_probe(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        const struct eqc_match_data *data;
        struct clk_hw_onecell_data *cells;
        unsigned int i, clk_count;
        struct resource *res;
        void __iomem *base;
        int ret;

        data = device_get_match_data(dev);
        if (!data)
                return 0; /* No clocks nor auxdevs, we are done. */

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res)
                return -ENODEV;

        base = ioremap(res->start, resource_size(res));
        if (!base)
                return -ENOMEM;

        /* Init optional reset auxiliary device. */
        if (data->reset_auxdev_name) {
                ret = eqc_auxdev_create(dev, base, data->reset_auxdev_name, 0);
                if (ret)
                        dev_warn(dev, "failed creating auxiliary device %s.%s: %d\n",
                                 KBUILD_MODNAME, data->reset_auxdev_name, ret);
        }

        /* Init optional pinctrl auxiliary device. */
        if (data->pinctrl_auxdev_name) {
                ret = eqc_auxdev_create(dev, base, data->pinctrl_auxdev_name, 0);
                if (ret)
                        dev_warn(dev, "failed creating auxiliary device %s.%s: %d\n",
                                 KBUILD_MODNAME, data->pinctrl_auxdev_name, ret);
        }

        if (data->pll_count + data->div_count + data->fixed_factor_count == 0)
                return 0; /* Zero clocks, we are done. */

        clk_count = data->pll_count + data->div_count +
                    data->fixed_factor_count + data->early_clk_count;
        cells = kzalloc_flex(*cells, hws, clk_count);
        if (!cells)
                return -ENOMEM;

        cells->num = clk_count;

        /* Early PLLs are marked as errors: the early provider will get queried. */
        for (i = 0; i < clk_count; i++)
                cells->hws[i] = ERR_PTR(-EINVAL);

        eqc_probe_init_plls(dev, data, base, cells);

        eqc_probe_init_divs(dev, data, base, cells);

        eqc_probe_init_fixed_factors(dev, data, cells);

        return of_clk_add_hw_provider(np, of_clk_hw_onecell_get, cells);
}

/* Required early for GIC timer (pll-cpu) and UARTs (pll-per). */
static const struct eqc_pll eqc_eyeq5_early_plls[] = {
        { .index = EQ5C_PLL_CPU, .name = "pll-cpu",  .reg64 = 0x02C },
        { .index = EQ5C_PLL_PER, .name = "pll-per",  .reg64 = 0x05C },
};

static const struct eqc_pll eqc_eyeq5_plls[] = {
        { .index = EQ5C_PLL_VMP,  .name = "pll-vmp",  .reg64 = 0x034 },
        { .index = EQ5C_PLL_PMA,  .name = "pll-pma",  .reg64 = 0x03C },
        { .index = EQ5C_PLL_VDI,  .name = "pll-vdi",  .reg64 = 0x044 },
        { .index = EQ5C_PLL_DDR0, .name = "pll-ddr0", .reg64 = 0x04C },
        { .index = EQ5C_PLL_PCI,  .name = "pll-pci",  .reg64 = 0x054 },
        { .index = EQ5C_PLL_PMAC, .name = "pll-pmac", .reg64 = 0x064 },
        { .index = EQ5C_PLL_MPC,  .name = "pll-mpc",  .reg64 = 0x06C },
        { .index = EQ5C_PLL_DDR1, .name = "pll-ddr1", .reg64 = 0x074 },
};

enum {
        /*
         * EQ5C_PLL_CPU children.
         * EQ5C_PER_OCC_PCI is the last clock exposed in dt-bindings.
         */
        EQ5C_CPU_OCC = EQ5C_PER_OCC_PCI + 1,
        EQ5C_CPU_SI_CSS0,
        EQ5C_CPU_CPC,
        EQ5C_CPU_CM,
        EQ5C_CPU_MEM,
        EQ5C_CPU_OCC_ISRAM,
        EQ5C_CPU_ISRAM,
        EQ5C_CPU_OCC_DBU,
        EQ5C_CPU_SI_DBU_TP,

        /*
         * EQ5C_PLL_VDI children.
         */
        EQ5C_VDI_OCC_VDI,
        EQ5C_VDI_VDI,
        EQ5C_VDI_OCC_CAN_SER,
        EQ5C_VDI_CAN_SER,
        EQ5C_VDI_I2C_SER,

        /*
         * EQ5C_PLL_PER children.
         */
        EQ5C_PER_PERIPH,
        EQ5C_PER_CAN,
        EQ5C_PER_TIMER,
        EQ5C_PER_CCF,
        EQ5C_PER_OCC_MJPEG,
        EQ5C_PER_HSM,
        EQ5C_PER_MJPEG,
        EQ5C_PER_FCMU_A,
};

static const struct eqc_fixed_factor eqc_eyeq5_early_fixed_factors[] = {
        /* EQ5C_PLL_CPU children */
        { EQ5C_CPU_OCC,         "occ-cpu",      1, 1,   EQ5C_PLL_CPU },
        { EQ5C_CPU_SI_CSS0,     "si-css0",      1, 1,   EQ5C_CPU_OCC },
        { EQ5C_CPU_CORE0,       "core0",        1, 1,   EQ5C_CPU_SI_CSS0 },
        { EQ5C_CPU_CORE1,       "core1",        1, 1,   EQ5C_CPU_SI_CSS0 },
        { EQ5C_CPU_CORE2,       "core2",        1, 1,   EQ5C_CPU_SI_CSS0 },
        { EQ5C_CPU_CORE3,       "core3",        1, 1,   EQ5C_CPU_SI_CSS0 },

        /* EQ5C_PLL_PER children */
        { EQ5C_PER_OCC,         "occ-periph",   1, 16,  EQ5C_PLL_PER },
        { EQ5C_PER_UART,        "uart",         1, 1,   EQ5C_PER_OCC },
};

static const struct eqc_fixed_factor eqc_eyeq5_fixed_factors[] = {
        /* EQ5C_PLL_CPU children */
        { EQ5C_CPU_CPC,         "cpc",          1, 1,   EQ5C_CPU_SI_CSS0 },
        { EQ5C_CPU_CM,          "cm",           1, 1,   EQ5C_CPU_SI_CSS0 },
        { EQ5C_CPU_MEM,         "mem",          1, 1,   EQ5C_CPU_SI_CSS0 },
        { EQ5C_CPU_OCC_ISRAM,   "occ-isram",    1, 2,   EQ5C_PLL_CPU },
        { EQ5C_CPU_ISRAM,       "isram",        1, 1,   EQ5C_CPU_OCC_ISRAM },
        { EQ5C_CPU_OCC_DBU,     "occ-dbu",      1, 10,  EQ5C_PLL_CPU },
        { EQ5C_CPU_SI_DBU_TP,   "si-dbu-tp",    1, 1,   EQ5C_CPU_OCC_DBU },

        /* EQ5C_PLL_VDI children */
        { EQ5C_VDI_OCC_VDI,     "occ-vdi",      1, 2,   EQ5C_PLL_VDI },
        { EQ5C_VDI_VDI,         "vdi",          1, 1,   EQ5C_VDI_OCC_VDI },
        { EQ5C_VDI_OCC_CAN_SER, "occ-can-ser",  1, 16,  EQ5C_PLL_VDI },
        { EQ5C_VDI_CAN_SER,     "can-ser",      1, 1,   EQ5C_VDI_OCC_CAN_SER },
        { EQ5C_VDI_I2C_SER,     "i2c-ser",      1, 20,  EQ5C_PLL_VDI },

        /* EQ5C_PLL_PER children */
        { EQ5C_PER_PERIPH,      "periph",       1, 1,   EQ5C_PER_OCC },
        { EQ5C_PER_CAN,         "can",          1, 1,   EQ5C_PER_OCC },
        { EQ5C_PER_SPI,         "spi",          1, 1,   EQ5C_PER_OCC },
        { EQ5C_PER_I2C,         "i2c",          1, 1,   EQ5C_PER_OCC },
        { EQ5C_PER_TIMER,       "timer",        1, 1,   EQ5C_PER_OCC },
        { EQ5C_PER_GPIO,        "gpio",         1, 1,   EQ5C_PER_OCC },
        { EQ5C_PER_EMMC,        "emmc-sys",     1, 10,  EQ5C_PLL_PER },
        { EQ5C_PER_CCF,         "ccf-ctrl",     1, 4,   EQ5C_PLL_PER },
        { EQ5C_PER_OCC_MJPEG,   "occ-mjpeg",    1, 2,   EQ5C_PLL_PER },
        { EQ5C_PER_HSM,         "hsm",          1, 1,   EQ5C_PER_OCC_MJPEG },
        { EQ5C_PER_MJPEG,       "mjpeg",        1, 1,   EQ5C_PER_OCC_MJPEG },
        { EQ5C_PER_FCMU_A,      "fcmu-a",       1, 20,  EQ5C_PLL_PER },
        { EQ5C_PER_OCC_PCI,     "occ-pci-sys",  1, 8,   EQ5C_PLL_PER },
};

static const struct eqc_div eqc_eyeq5_divs[] = {
        {
                .index = EQ5C_DIV_OSPI,
                .name = "div-ospi",
                .parent = EQ5C_PLL_PER,
                .reg = 0x11C,
                .shift = 0,
                .width = 4,
        },
};

static const struct eqc_early_match_data eqc_eyeq5_early_match_data __initconst = {
        .early_pll_count        = ARRAY_SIZE(eqc_eyeq5_early_plls),
        .early_plls             = eqc_eyeq5_early_plls,

        .early_fixed_factor_count       = ARRAY_SIZE(eqc_eyeq5_early_fixed_factors),
        .early_fixed_factors            = eqc_eyeq5_early_fixed_factors,

        .late_clk_count         = ARRAY_SIZE(eqc_eyeq5_plls) + ARRAY_SIZE(eqc_eyeq5_divs) +
                                  ARRAY_SIZE(eqc_eyeq5_fixed_factors),
};

static const struct eqc_match_data eqc_eyeq5_match_data = {
        .pll_count      = ARRAY_SIZE(eqc_eyeq5_plls),
        .plls           = eqc_eyeq5_plls,

        .div_count      = ARRAY_SIZE(eqc_eyeq5_divs),
        .divs           = eqc_eyeq5_divs,

        .fixed_factor_count     = ARRAY_SIZE(eqc_eyeq5_fixed_factors),
        .fixed_factors          = eqc_eyeq5_fixed_factors,

        .reset_auxdev_name = "reset",
        .pinctrl_auxdev_name = "pinctrl",

        .early_clk_count = ARRAY_SIZE(eqc_eyeq5_early_plls) +
                           ARRAY_SIZE(eqc_eyeq5_early_fixed_factors),
};

static const struct eqc_pll eqc_eyeq6l_plls[] = {
        { .index = EQ6LC_PLL_DDR, .name = "pll-ddr", .reg64 = 0x02C },
        { .index = EQ6LC_PLL_CPU, .name = "pll-cpu", .reg64 = 0x034 }, /* also acc */
        { .index = EQ6LC_PLL_PER, .name = "pll-per", .reg64 = 0x03C },
        { .index = EQ6LC_PLL_VDI, .name = "pll-vdi", .reg64 = 0x044 },
};

static const struct eqc_match_data eqc_eyeq6l_match_data = {
        .pll_count      = ARRAY_SIZE(eqc_eyeq6l_plls),
        .plls           = eqc_eyeq6l_plls,

        .reset_auxdev_name = "reset",
};

static const struct eqc_match_data eqc_eyeq6h_west_match_data = {
        .reset_auxdev_name = "reset_west",
};

static const struct eqc_pll eqc_eyeq6h_east_plls[] = {
        { .index = 0, .name = "pll-east", .reg64 = 0x074 },
};

static const struct eqc_match_data eqc_eyeq6h_east_match_data = {
        .pll_count      = ARRAY_SIZE(eqc_eyeq6h_east_plls),
        .plls           = eqc_eyeq6h_east_plls,

        .reset_auxdev_name = "reset_east",
};

static const struct eqc_pll eqc_eyeq6h_south_plls[] = {
        { .index = EQ6HC_SOUTH_PLL_VDI,  .name = "pll-vdi",  .reg64 = 0x000 },
        { .index = EQ6HC_SOUTH_PLL_PCIE, .name = "pll-pcie", .reg64 = 0x008 },
        { .index = EQ6HC_SOUTH_PLL_PER,  .name = "pll-per",  .reg64 = 0x010 },
        { .index = EQ6HC_SOUTH_PLL_ISP,  .name = "pll-isp",  .reg64 = 0x018 },
};

static const struct eqc_div eqc_eyeq6h_south_divs[] = {
        {
                .index = EQ6HC_SOUTH_DIV_EMMC,
                .name = "div-emmc",
                .parent = EQ6HC_SOUTH_PLL_PER,
                .reg = 0x070,
                .shift = 4,
                .width = 4,
        },
        {
                .index = EQ6HC_SOUTH_DIV_OSPI_REF,
                .name = "div-ospi-ref",
                .parent = EQ6HC_SOUTH_PLL_PER,
                .reg = 0x090,
                .shift = 4,
                .width = 4,
        },
        {
                .index = EQ6HC_SOUTH_DIV_OSPI_SYS,
                .name = "div-ospi-sys",
                .parent = EQ6HC_SOUTH_PLL_PER,
                .reg = 0x090,
                .shift = 8,
                .width = 1,
        },
        {
                .index = EQ6HC_SOUTH_DIV_TSU,
                .name = "div-tsu",
                .parent = EQ6HC_SOUTH_PLL_PCIE,
                .reg = 0x098,
                .shift = 4,
                .width = 8,
        },
};

static const struct eqc_match_data eqc_eyeq6h_south_match_data = {
        .pll_count      = ARRAY_SIZE(eqc_eyeq6h_south_plls),
        .plls           = eqc_eyeq6h_south_plls,

        .div_count      = ARRAY_SIZE(eqc_eyeq6h_south_divs),
        .divs           = eqc_eyeq6h_south_divs,
};

static const struct eqc_pll eqc_eyeq6h_ddr0_plls[] = {
        { .index = 0, .name = "pll-ddr0", .reg64 = 0x074 },
};

static const struct eqc_match_data eqc_eyeq6h_ddr0_match_data = {
        .pll_count      = ARRAY_SIZE(eqc_eyeq6h_ddr0_plls),
        .plls           = eqc_eyeq6h_ddr0_plls,
};

static const struct eqc_pll eqc_eyeq6h_ddr1_plls[] = {
        { .index = 0, .name = "pll-ddr1", .reg64 = 0x074 },
};

static const struct eqc_match_data eqc_eyeq6h_ddr1_match_data = {
        .pll_count      = ARRAY_SIZE(eqc_eyeq6h_ddr1_plls),
        .plls           = eqc_eyeq6h_ddr1_plls,
};

static const struct eqc_pll eqc_eyeq6h_acc_plls[] = {
        { .index = EQ6HC_ACC_PLL_XNN, .name = "pll-xnn", .reg64 = 0x040 },
        { .index = EQ6HC_ACC_PLL_VMP, .name = "pll-vmp", .reg64 = 0x050 },
        { .index = EQ6HC_ACC_PLL_PMA, .name = "pll-pma", .reg64 = 0x05C },
        { .index = EQ6HC_ACC_PLL_MPC, .name = "pll-mpc", .reg64 = 0x068 },
        { .index = EQ6HC_ACC_PLL_NOC, .name = "pll-noc", .reg64 = 0x070 },
};

static const struct eqc_match_data eqc_eyeq6h_acc_match_data = {
        .pll_count      = ARRAY_SIZE(eqc_eyeq6h_acc_plls),
        .plls           = eqc_eyeq6h_acc_plls,

        .reset_auxdev_name = "reset_acc",
};

static const struct of_device_id eqc_match_table[] = {
        { .compatible = "mobileye,eyeq5-olb", .data = &eqc_eyeq5_match_data },
        { .compatible = "mobileye,eyeq6l-olb", .data = &eqc_eyeq6l_match_data },
        { .compatible = "mobileye,eyeq6h-west-olb", .data = &eqc_eyeq6h_west_match_data },
        { .compatible = "mobileye,eyeq6h-east-olb", .data = &eqc_eyeq6h_east_match_data },
        { .compatible = "mobileye,eyeq6h-south-olb", .data = &eqc_eyeq6h_south_match_data },
        { .compatible = "mobileye,eyeq6h-ddr0-olb", .data = &eqc_eyeq6h_ddr0_match_data },
        { .compatible = "mobileye,eyeq6h-ddr1-olb", .data = &eqc_eyeq6h_ddr1_match_data },
        { .compatible = "mobileye,eyeq6h-acc-olb", .data = &eqc_eyeq6h_acc_match_data },
        {}
};

static struct platform_driver eqc_driver = {
        .probe = eqc_probe,
        .driver = {
                .name = "clk-eyeq",
                .of_match_table = eqc_match_table,
                .suppress_bind_attrs = true,
        },
};
builtin_platform_driver(eqc_driver);

/* Required early for GIC timer. */
static const struct eqc_pll eqc_eyeq6h_central_early_plls[] = {
        { .index = EQ6HC_CENTRAL_PLL_CPU, .name = "pll-cpu", .reg64 = 0x02C },
};

static const struct eqc_fixed_factor eqc_eyeq6h_central_early_fixed_factors[] = {
        { EQ6HC_CENTRAL_CPU_OCC, "occ-cpu", 1, 1, EQ6HC_CENTRAL_PLL_CPU },
};

static const struct eqc_early_match_data eqc_eyeq6h_central_early_match_data __initconst = {
        .early_pll_count        = ARRAY_SIZE(eqc_eyeq6h_central_early_plls),
        .early_plls             = eqc_eyeq6h_central_early_plls,

        .early_fixed_factor_count = ARRAY_SIZE(eqc_eyeq6h_central_early_fixed_factors),
        .early_fixed_factors = eqc_eyeq6h_central_early_fixed_factors,
};

/* Required early for UART. */
static const struct eqc_pll eqc_eyeq6h_west_early_plls[] = {
        { .index = EQ6HC_WEST_PLL_PER, .name = "pll-west", .reg64 = 0x074 },
};

static const struct eqc_fixed_factor eqc_eyeq6h_west_early_fixed_factors[] = {
        { EQ6HC_WEST_PER_OCC,  "west-per-occ",  1, 10, EQ6HC_WEST_PLL_PER },
        { EQ6HC_WEST_PER_UART, "west-per-uart", 1, 1,  EQ6HC_WEST_PER_OCC },
};

static const struct eqc_early_match_data eqc_eyeq6h_west_early_match_data __initconst = {
        .early_pll_count        = ARRAY_SIZE(eqc_eyeq6h_west_early_plls),
        .early_plls             = eqc_eyeq6h_west_early_plls,

        .early_fixed_factor_count = ARRAY_SIZE(eqc_eyeq6h_west_early_fixed_factors),
        .early_fixed_factors = eqc_eyeq6h_west_early_fixed_factors,
};

static void __init eqc_early_init(struct device_node *np,
                                  const struct eqc_early_match_data *early_data)
{
        struct clk_hw_onecell_data *cells;
        unsigned int i, clk_count;
        void __iomem *base;
        int ret;

        clk_count = early_data->early_pll_count + early_data->early_fixed_factor_count +
                    early_data->late_clk_count;
        cells = kzalloc_flex(*cells, hws, clk_count);
        if (!cells) {
                ret = -ENOMEM;
                goto err;
        }

        cells->num = clk_count;

        /*
         * Mark all clocks as deferred; some are registered here, the rest at
         * platform device probe.
         *
         * Once the platform device is probed, its provider will take priority
         * when looking up clocks.
         */
        for (i = 0; i < clk_count; i++)
                cells->hws[i] = ERR_PTR(-EPROBE_DEFER);

        /* Offsets (reg64) of early PLLs are relative to OLB block. */
        base = of_iomap(np, 0);
        if (!base) {
                ret = -ENODEV;
                goto err;
        }

        for (i = 0; i < early_data->early_pll_count; i++) {
                const struct eqc_pll *pll = &early_data->early_plls[i];
                unsigned long mult, div, acc;
                struct clk_hw *hw;
                u32 r0, r1;
                u64 val;

                val = readq(base + pll->reg64);
                r0 = val;
                r1 = val >> 32;

                ret = eqc_pll_parse_registers(r0, r1, &mult, &div, &acc);
                if (ret) {
                        pr_err("failed parsing state of %s\n", pll->name);
                        goto err;
                }

                hw = clk_hw_register_fixed_factor_with_accuracy_fwname(NULL,
                                np, pll->name, "ref", 0, mult, div, acc);
                cells->hws[pll->index] = hw;
                if (IS_ERR(hw)) {
                        pr_err("failed registering %s: %pe\n", pll->name, hw);
                        ret = PTR_ERR(hw);
                        goto err;
                }
        }

        for (i = 0; i < early_data->early_fixed_factor_count; i++) {
                const struct eqc_fixed_factor *ff = &early_data->early_fixed_factors[i];
                struct clk_hw *parent_hw = cells->hws[ff->parent];
                struct clk_hw *hw;

                hw = clk_hw_register_fixed_factor_parent_hw(NULL, ff->name,
                                parent_hw, 0, ff->mult, ff->div);
                cells->hws[ff->index] = hw;
                if (IS_ERR(hw)) {
                        pr_err("failed registering %s: %pe\n", ff->name, hw);
                        ret = PTR_ERR(hw);
                        goto err;
                }
        }

        ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, cells);
        if (ret) {
                pr_err("failed registering clk provider: %d\n", ret);
                goto err;
        }

        return;

err:
        /*
         * We are doomed. The system will not be able to boot.
         *
         * Let's still try to be good citizens by freeing resources and print
         * a last error message that might help debugging.
         */

        pr_err("failed clk init: %d\n", ret);

        if (cells) {
                of_clk_del_provider(np);

                for (i = 0; i < early_data->early_pll_count; i++) {
                        const struct eqc_pll *pll = &early_data->early_plls[i];
                        struct clk_hw *hw = cells->hws[pll->index];

                        if (!IS_ERR_OR_NULL(hw))
                                clk_hw_unregister_fixed_factor(hw);
                }

                kfree(cells);
        }
}

static void __init eqc_eyeq5_early_init(struct device_node *np)
{
        eqc_early_init(np, &eqc_eyeq5_early_match_data);
}
CLK_OF_DECLARE_DRIVER(eqc_eyeq5, "mobileye,eyeq5-olb", eqc_eyeq5_early_init);

static void __init eqc_eyeq6h_central_early_init(struct device_node *np)
{
        eqc_early_init(np, &eqc_eyeq6h_central_early_match_data);
}
CLK_OF_DECLARE_DRIVER(eqc_eyeq6h_central, "mobileye,eyeq6h-central-olb",
                      eqc_eyeq6h_central_early_init);

static void __init eqc_eyeq6h_west_early_init(struct device_node *np)
{
        eqc_early_init(np, &eqc_eyeq6h_west_early_match_data);
}
CLK_OF_DECLARE_DRIVER(eqc_eyeq6h_west, "mobileye,eyeq6h-west-olb",
                      eqc_eyeq6h_west_early_init);