root/drivers/clk/clk-cs2000-cp.c
// SPDX-License-Identifier: GPL-2.0
/*
 * CS2000  --  CIRRUS LOGIC Fractional-N Clock Synthesizer & Clock Multiplier
 *
 * Copyright (C) 2015 Renesas Electronics Corporation
 * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
 */
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/regmap.h>

#define CH_MAX 4
#define RATIO_REG_SIZE 4

#define DEVICE_ID       0x1
#define DEVICE_CTRL     0x2
#define DEVICE_CFG1     0x3
#define DEVICE_CFG2     0x4
#define GLOBAL_CFG      0x5
#define Ratio_Add(x, nth)       (6 + (x * 4) + (nth))
#define Ratio_Val(x, nth)       ((x >> (24 - (8 * nth))) & 0xFF)
#define Val_Ratio(x, nth)       ((x & 0xFF) << (24 - (8 * nth)))
#define FUNC_CFG1       0x16
#define FUNC_CFG2       0x17

/* DEVICE_ID */
#define REVISION_MASK   (0x7)
#define REVISION_B2_B3  (0x4)
#define REVISION_C1     (0x6)

/* DEVICE_CTRL */
#define PLL_UNLOCK      (1 << 7)
#define AUXOUTDIS       (1 << 1)
#define CLKOUTDIS       (1 << 0)

/* DEVICE_CFG1 */
#define RSEL(x)         (((x) & 0x3) << 3)
#define RSEL_MASK       RSEL(0x3)
#define AUXOUTSRC(x)    (((x) & 0x3) << 1)
#define AUXOUTSRC_MASK  AUXOUTSRC(0x3)
#define ENDEV1          (0x1)

/* DEVICE_CFG2 */
#define AUTORMOD        (1 << 3)
#define LOCKCLK(x)      (((x) & 0x3) << 1)
#define LOCKCLK_MASK    LOCKCLK(0x3)
#define FRACNSRC_MASK   (1 << 0)
#define FRACNSRC_STATIC         (0 << 0)
#define FRACNSRC_DYNAMIC        (1 << 0)

/* GLOBAL_CFG */
#define FREEZE          (1 << 7)
#define ENDEV2          (0x1)

/* FUNC_CFG1 */
#define CLKSKIPEN       (1 << 7)
#define REFCLKDIV(x)    (((x) & 0x3) << 3)
#define REFCLKDIV_MASK  REFCLKDIV(0x3)

/* FUNC_CFG2 */
#define LFRATIO_MASK    (1 << 3)
#define LFRATIO_20_12   (0 << 3)
#define LFRATIO_12_20   (1 << 3)

#define CH_SIZE_ERR(ch)         ((ch < 0) || (ch >= CH_MAX))
#define hw_to_priv(_hw)         container_of(_hw, struct cs2000_priv, hw)
#define priv_to_client(priv)    (priv->client)
#define priv_to_dev(priv)       (&(priv_to_client(priv)->dev))

#define CLK_IN  0
#define REF_CLK 1
#define CLK_MAX 2

static bool cs2000_readable_reg(struct device *dev, unsigned int reg)
{
        return reg > 0;
}

static bool cs2000_writeable_reg(struct device *dev, unsigned int reg)
{
        return reg != DEVICE_ID;
}

static bool cs2000_volatile_reg(struct device *dev, unsigned int reg)
{
        return reg == DEVICE_CTRL;
}

static const struct regmap_config cs2000_regmap_config = {
        .reg_bits       = 8,
        .val_bits       = 8,
        .max_register   = FUNC_CFG2,
        .readable_reg   = cs2000_readable_reg,
        .writeable_reg  = cs2000_writeable_reg,
        .volatile_reg   = cs2000_volatile_reg,
};

struct cs2000_priv {
        struct clk_hw hw;
        struct i2c_client *client;
        struct clk *clk_in;
        struct clk *ref_clk;
        struct regmap *regmap;

        bool dynamic_mode;
        bool lf_ratio;
        bool clk_skip;

        /* suspend/resume */
        unsigned long saved_rate;
        unsigned long saved_parent_rate;
};

static const struct of_device_id cs2000_of_match[] = {
        { .compatible = "cirrus,cs2000-cp", },
        {},
};
MODULE_DEVICE_TABLE(of, cs2000_of_match);

static const struct i2c_device_id cs2000_id[] = {
        { "cs2000-cp", },
        {}
};
MODULE_DEVICE_TABLE(i2c, cs2000_id);

static int cs2000_enable_dev_config(struct cs2000_priv *priv, bool enable)
{
        int ret;

        ret = regmap_update_bits(priv->regmap, DEVICE_CFG1, ENDEV1,
                                 enable ? ENDEV1 : 0);
        if (ret < 0)
                return ret;

        ret = regmap_update_bits(priv->regmap, GLOBAL_CFG,  ENDEV2,
                                 enable ? ENDEV2 : 0);
        if (ret < 0)
                return ret;

        ret = regmap_update_bits(priv->regmap, FUNC_CFG1, CLKSKIPEN,
                                 (enable && priv->clk_skip) ? CLKSKIPEN : 0);
        if (ret < 0)
                return ret;

        return 0;
}

static int cs2000_ref_clk_bound_rate(struct cs2000_priv *priv,
                                     u32 rate_in)
{
        u32 val;

        if (rate_in >= 32000000 && rate_in < 56000000)
                val = 0x0;
        else if (rate_in >= 16000000 && rate_in < 28000000)
                val = 0x1;
        else if (rate_in >= 8000000 && rate_in < 14000000)
                val = 0x2;
        else
                return -EINVAL;

        return regmap_update_bits(priv->regmap, FUNC_CFG1,
                                  REFCLKDIV_MASK,
                                  REFCLKDIV(val));
}

static int cs2000_wait_pll_lock(struct cs2000_priv *priv)
{
        struct device *dev = priv_to_dev(priv);
        unsigned int i, val;
        int ret;

        for (i = 0; i < 256; i++) {
                ret = regmap_read(priv->regmap, DEVICE_CTRL, &val);
                if (ret < 0)
                        return ret;
                if (!(val & PLL_UNLOCK))
                        return 0;
                udelay(1);
        }

        dev_err(dev, "pll lock failed\n");

        return -ETIMEDOUT;
}

static int cs2000_clk_out_enable(struct cs2000_priv *priv, bool enable)
{
        /* enable both AUX_OUT, CLK_OUT */
        return regmap_update_bits(priv->regmap, DEVICE_CTRL,
                                  (AUXOUTDIS | CLKOUTDIS),
                                  enable ? 0 :
                                  (AUXOUTDIS | CLKOUTDIS));
}

static u32 cs2000_rate_to_ratio(u32 rate_in, u32 rate_out, bool lf_ratio)
{
        u64 ratio;
        u32 multiplier = lf_ratio ? 12 : 20;

        /*
         * ratio = rate_out / rate_in * 2^multiplier
         *
         * To avoid over flow, rate_out is u64.
         * The result should be u32.
         */
        ratio = (u64)rate_out << multiplier;
        do_div(ratio, rate_in);

        return ratio;
}

static unsigned long cs2000_ratio_to_rate(u32 ratio, u32 rate_in, bool lf_ratio)
{
        u64 rate_out;
        u32 multiplier = lf_ratio ? 12 : 20;

        /*
         * ratio = rate_out / rate_in * 2^multiplier
         *
         * To avoid over flow, rate_out is u64.
         * The result should be u32 or unsigned long.
         */

        rate_out = (u64)ratio * rate_in;
        return rate_out >> multiplier;
}

static int cs2000_ratio_set(struct cs2000_priv *priv,
                            int ch, u32 rate_in, u32 rate_out)
{
        u32 val;
        unsigned int i;
        int ret;

        if (CH_SIZE_ERR(ch))
                return -EINVAL;

        val = cs2000_rate_to_ratio(rate_in, rate_out, priv->lf_ratio);
        for (i = 0; i < RATIO_REG_SIZE; i++) {
                ret = regmap_write(priv->regmap,
                                   Ratio_Add(ch, i),
                                   Ratio_Val(val, i));
                if (ret < 0)
                        return ret;
        }

        return 0;
}

static u32 cs2000_ratio_get(struct cs2000_priv *priv, int ch)
{
        unsigned int tmp, i;
        u32 val;
        int ret;

        val = 0;
        for (i = 0; i < RATIO_REG_SIZE; i++) {
                ret = regmap_read(priv->regmap, Ratio_Add(ch, i), &tmp);
                if (ret < 0)
                        return 0;

                val |= Val_Ratio(tmp, i);
        }

        return val;
}

static int cs2000_ratio_select(struct cs2000_priv *priv, int ch)
{
        int ret;
        u8 fracnsrc;

        if (CH_SIZE_ERR(ch))
                return -EINVAL;

        ret = regmap_update_bits(priv->regmap, DEVICE_CFG1, RSEL_MASK, RSEL(ch));
        if (ret < 0)
                return ret;

        fracnsrc = priv->dynamic_mode ? FRACNSRC_DYNAMIC : FRACNSRC_STATIC;

        ret = regmap_update_bits(priv->regmap, DEVICE_CFG2,
                                 AUTORMOD | LOCKCLK_MASK | FRACNSRC_MASK,
                                 LOCKCLK(ch) | fracnsrc);
        if (ret < 0)
                return ret;

        return 0;
}

static unsigned long cs2000_recalc_rate(struct clk_hw *hw,
                                        unsigned long parent_rate)
{
        struct cs2000_priv *priv = hw_to_priv(hw);
        int ch = 0; /* it uses ch0 only at this point */
        u32 ratio;

        ratio = cs2000_ratio_get(priv, ch);

        return cs2000_ratio_to_rate(ratio, parent_rate, priv->lf_ratio);
}

static int cs2000_determine_rate(struct clk_hw *hw,
                                 struct clk_rate_request *req)
{
        struct cs2000_priv *priv = hw_to_priv(hw);
        u32 ratio;

        ratio = cs2000_rate_to_ratio(req->best_parent_rate, req->rate,
                                     priv->lf_ratio);

        req->rate = cs2000_ratio_to_rate(ratio, req->best_parent_rate,
                                         priv->lf_ratio);

        return 0;
}

static int cs2000_select_ratio_mode(struct cs2000_priv *priv,
                                    unsigned long rate,
                                    unsigned long parent_rate)
{
        /*
         * From the datasheet:
         *
         * | It is recommended that the 12.20 High-Resolution format be
         * | utilized whenever the desired ratio is less than 4096 since
         * | the output frequency accuracy of the PLL is directly proportional
         * | to the accuracy of the timing reference clock and the resolution
         * | of the R_UD.
         *
         * This mode is only available in dynamic mode.
         */
        priv->lf_ratio = priv->dynamic_mode && ((rate / parent_rate) > 4096);

        return regmap_update_bits(priv->regmap, FUNC_CFG2, LFRATIO_MASK,
                                  priv->lf_ratio ? LFRATIO_20_12 : LFRATIO_12_20);
}

static int __cs2000_set_rate(struct cs2000_priv *priv, int ch,
                             unsigned long rate, unsigned long parent_rate)

{
        int ret;

        ret = regmap_update_bits(priv->regmap, GLOBAL_CFG, FREEZE, FREEZE);
        if (ret < 0)
                return ret;

        ret = cs2000_select_ratio_mode(priv, rate, parent_rate);
        if (ret < 0)
                return ret;

        ret = cs2000_ratio_set(priv, ch, parent_rate, rate);
        if (ret < 0)
                return ret;

        ret = cs2000_ratio_select(priv, ch);
        if (ret < 0)
                return ret;

        ret = regmap_update_bits(priv->regmap, GLOBAL_CFG, FREEZE, 0);
        if (ret < 0)
                return ret;

        priv->saved_rate        = rate;
        priv->saved_parent_rate = parent_rate;

        return 0;
}

static int cs2000_set_rate(struct clk_hw *hw,
                           unsigned long rate, unsigned long parent_rate)
{
        struct cs2000_priv *priv = hw_to_priv(hw);
        int ch = 0; /* it uses ch0 only at this point */

        return __cs2000_set_rate(priv, ch, rate, parent_rate);
}

static int cs2000_set_saved_rate(struct cs2000_priv *priv)
{
        int ch = 0; /* it uses ch0 only at this point */

        return __cs2000_set_rate(priv, ch,
                                 priv->saved_rate,
                                 priv->saved_parent_rate);
}

static int cs2000_enable(struct clk_hw *hw)
{
        struct cs2000_priv *priv = hw_to_priv(hw);
        int ret;

        ret = cs2000_enable_dev_config(priv, true);
        if (ret < 0)
                return ret;

        ret = cs2000_clk_out_enable(priv, true);
        if (ret < 0)
                return ret;

        ret = cs2000_wait_pll_lock(priv);
        if (ret < 0)
                return ret;

        return ret;
}

static void cs2000_disable(struct clk_hw *hw)
{
        struct cs2000_priv *priv = hw_to_priv(hw);

        cs2000_enable_dev_config(priv, false);

        cs2000_clk_out_enable(priv, false);
}

static u8 cs2000_get_parent(struct clk_hw *hw)
{
        struct cs2000_priv *priv = hw_to_priv(hw);

        /*
         * In dynamic mode, output rates are derived from CLK_IN.
         * In static mode, CLK_IN is ignored, so we return REF_CLK instead.
         */
        return priv->dynamic_mode ? CLK_IN : REF_CLK;
}

static const struct clk_ops cs2000_ops = {
        .get_parent     = cs2000_get_parent,
        .recalc_rate    = cs2000_recalc_rate,
        .determine_rate = cs2000_determine_rate,
        .set_rate       = cs2000_set_rate,
        .prepare        = cs2000_enable,
        .unprepare      = cs2000_disable,
};

static int cs2000_clk_get(struct cs2000_priv *priv)
{
        struct device *dev = priv_to_dev(priv);
        struct clk *clk_in, *ref_clk;

        clk_in = devm_clk_get(dev, "clk_in");
        /* not yet provided */
        if (IS_ERR(clk_in))
                return -EPROBE_DEFER;

        ref_clk = devm_clk_get(dev, "ref_clk");
        /* not yet provided */
        if (IS_ERR(ref_clk))
                return -EPROBE_DEFER;

        priv->clk_in    = clk_in;
        priv->ref_clk   = ref_clk;

        return 0;
}

static int cs2000_clk_register(struct cs2000_priv *priv)
{
        struct device *dev = priv_to_dev(priv);
        struct device_node *np = dev->of_node;
        struct clk_init_data init;
        const char *name = np->name;
        static const char *parent_names[CLK_MAX];
        u32 aux_out = 0;
        int ref_clk_rate;
        int ch = 0; /* it uses ch0 only at this point */
        int ret;

        of_property_read_string(np, "clock-output-names", &name);

        priv->dynamic_mode = of_property_read_bool(np, "cirrus,dynamic-mode");
        dev_info(dev, "operating in %s mode\n",
                 priv->dynamic_mode ? "dynamic" : "static");

        of_property_read_u32(np, "cirrus,aux-output-source", &aux_out);
        ret = regmap_update_bits(priv->regmap, DEVICE_CFG1,
                                 AUXOUTSRC_MASK, AUXOUTSRC(aux_out));
        if (ret < 0)
                return ret;

        priv->clk_skip = of_property_read_bool(np, "cirrus,clock-skip");

        ref_clk_rate = clk_get_rate(priv->ref_clk);
        ret = cs2000_ref_clk_bound_rate(priv, ref_clk_rate);
        if (ret < 0)
                return ret;

        if (priv->dynamic_mode) {
                /* Default to low-frequency mode to allow for large ratios */
                priv->lf_ratio = true;
        } else {
                /*
                 * set default rate as 1/1.
                 * otherwise .set_rate which setup ratio
                 * is never called if user requests 1/1 rate
                 */
                ret = __cs2000_set_rate(priv, ch, ref_clk_rate, ref_clk_rate);
                if (ret < 0)
                        return ret;
        }

        parent_names[CLK_IN]    = __clk_get_name(priv->clk_in);
        parent_names[REF_CLK]   = __clk_get_name(priv->ref_clk);

        init.name               = name;
        init.ops                = &cs2000_ops;
        init.flags              = CLK_SET_RATE_GATE;
        init.parent_names       = parent_names;
        init.num_parents        = ARRAY_SIZE(parent_names);

        priv->hw.init = &init;

        ret = clk_hw_register(dev, &priv->hw);
        if (ret)
                return ret;

        ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &priv->hw);
        if (ret < 0) {
                clk_hw_unregister(&priv->hw);
                return ret;
        }

        return 0;
}

static int cs2000_version_print(struct cs2000_priv *priv)
{
        struct device *dev = priv_to_dev(priv);
        const char *revision;
        unsigned int val;
        int ret;

        ret = regmap_read(priv->regmap, DEVICE_ID, &val);
        if (ret < 0)
                return ret;

        /* CS2000 should be 0x0 */
        if (val >> 3)
                return -EIO;

        switch (val & REVISION_MASK) {
        case REVISION_B2_B3:
                revision = "B2 / B3";
                break;
        case REVISION_C1:
                revision = "C1";
                break;
        default:
                return -EIO;
        }

        dev_info(dev, "revision - %s\n", revision);

        return 0;
}

static void cs2000_remove(struct i2c_client *client)
{
        struct cs2000_priv *priv = i2c_get_clientdata(client);
        struct device *dev = priv_to_dev(priv);
        struct device_node *np = dev->of_node;

        of_clk_del_provider(np);

        clk_hw_unregister(&priv->hw);
}

static int cs2000_probe(struct i2c_client *client)
{
        struct cs2000_priv *priv;
        struct device *dev = &client->dev;
        int ret;

        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;

        priv->client = client;
        i2c_set_clientdata(client, priv);

        priv->regmap = devm_regmap_init_i2c(client, &cs2000_regmap_config);
        if (IS_ERR(priv->regmap))
                return PTR_ERR(priv->regmap);

        ret = cs2000_clk_get(priv);
        if (ret < 0)
                return ret;

        ret = cs2000_clk_register(priv);
        if (ret < 0)
                return ret;

        ret = cs2000_version_print(priv);
        if (ret < 0)
                goto probe_err;

        return 0;

probe_err:
        cs2000_remove(client);

        return ret;
}

static int __maybe_unused cs2000_resume(struct device *dev)
{
        struct cs2000_priv *priv = dev_get_drvdata(dev);

        return cs2000_set_saved_rate(priv);
}

static const struct dev_pm_ops cs2000_pm_ops = {
        SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, cs2000_resume)
};

static struct i2c_driver cs2000_driver = {
        .driver = {
                .name = "cs2000-cp",
                .pm     = &cs2000_pm_ops,
                .of_match_table = cs2000_of_match,
        },
        .probe          = cs2000_probe,
        .remove         = cs2000_remove,
        .id_table       = cs2000_id,
};

module_i2c_driver(cs2000_driver);

MODULE_DESCRIPTION("CS2000-CP driver");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
MODULE_LICENSE("GPL v2");