root/drivers/phy/ti/phy-ds125df111.c
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2026 NXP */

#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/i2c.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
#include <linux/phy/phy.h>
#include <linux/slab.h>

#define DS125DF111_NUM_CH                       2
#define DS125DF111_NUM_VCO_GROUP_REG            5

#define DS125DF111_CH_SELECT                    0xff
#define DS125DF111_CH_SELECT_TARGET_MASK        GENMASK(3, 0)
#define DS125DF111_CH_SELECT_EN                 BIT(2)

#define DS125DF111_CH_CTRL                      0x00
#define DS125DF111_CH_CTRL_RESET                BIT(2) /* self clearing */

#define DS125DF111_CH_RST_SLEEP_US              10
#define DS125DF111_CH_RST_TIMEOUT_US            10000

#define DS125DF111_VCO_GROUP_BASE               0x60

#define DS125DF111_RATIOS                       0x2f
#define DS125DF111_RATIOS_RATE_MASK             GENMASK(7, 6)
#define DS125DF111_RATIOS_SUBRATE_MASK          GENMASK(5, 4)
#define DS125DF111_RATIOS_MASK                  GENMASK(7, 4)

struct ds125df111_ch {
        struct phy *phy;
        struct ds125df111_priv *priv;
        int idx;
};

struct ds125df111_priv {
        struct ds125df111_ch ch[DS125DF111_NUM_CH];
        struct i2c_client *client;
        struct mutex mutex; /* protects access to shared registers */
};

enum ds125df111_mode {
        FREQ_1G,
        FREQ_10G,
};

static const struct ds125df111_config {
        u8 vco_group[DS125DF111_NUM_VCO_GROUP_REG];
        u8 rate;
        u8 subrate;
} ds125df111_cfg[] = {
        [FREQ_1G] = {
                /* VCO group #0 = 10GHz, VCO group #1 = 10GHz */
                .vco_group = {0x00, 0xB2, 0x00, 0xB2, 0xCC},
                /* By using the following combination of rate and subrate we
                 * select divide ratios of 1, 2, 4, 8 on both groups
                 */
                .rate = 0x1,
                .subrate = 0x2,
        },

        [FREQ_10G] = {
                /* VCO group #0 = 10.3125GHz, VCO group #1 = 10.3125GHz */
                .vco_group = {0x90, 0xB3, 0x90, 0xB3, 0xCD},
                /* By using the following combination of rate and subrate we
                 * select divide ratios of 1 on both groups
                 */
                .rate = 0x1,
                .subrate = 0x3,
        },
};

static int ds125df111_rmw(struct ds125df111_priv *priv, u8 reg, u8 clr, u8 set)
{
        struct i2c_client *i2c = priv->client;
        int err;
        u8 val;

        err = i2c_smbus_read_byte_data(i2c, reg);
        if (err < 0)
                return err;

        val = (u8)err;
        val &= ~clr;
        val |= set;

        err = i2c_smbus_write_byte_data(i2c, reg, val);
        if (err < 0)
                return err;

        return 0;
}

static int ds125df111_configure(struct phy *phy,
                                const struct ds125df111_config *cfg)
{
        struct ds125df111_ch *ch = phy_get_drvdata(phy);
        struct ds125df111_priv *priv = ch->priv;
        struct i2c_client *i2c = priv->client;
        struct device *dev = &phy->dev;
        u8 ratios_val;
        int err, i;
        int val;

        mutex_lock(&priv->mutex);

        /* Make sure that any subsequent read/write operation will be directed
         * only to the registers of the selected channel
         */
        err = ds125df111_rmw(priv, DS125DF111_CH_SELECT,
                             DS125DF111_CH_SELECT_TARGET_MASK,
                             DS125DF111_CH_SELECT_EN | ch->idx);
        if (err < 0) {
                dev_err(dev, "Unable to select channel: %pe\n", ERR_PTR(err));
                goto out;
        }

        /* Reset channel registers and wait until the bit was cleared */
        err = ds125df111_rmw(priv, DS125DF111_CH_CTRL, 0,
                             DS125DF111_CH_CTRL_RESET);
        if (err < 0) {
                dev_err(dev, "Error resetting channel configuration: %pe\n",
                        ERR_PTR(err));
                goto out;
        }

        err = read_poll_timeout(i2c_smbus_read_byte_data, val,
                                val < 0 || !(val & DS125DF111_CH_CTRL_RESET),
                                DS125DF111_CH_RST_SLEEP_US,
                                DS125DF111_CH_RST_TIMEOUT_US, false, i2c,
                                DS125DF111_CH_CTRL);
        if (err) {
                dev_err(dev, "Timed out waiting for channel reset: %pe\n",
                        ERR_PTR(err));
                goto out;
        }

        if (val < 0) {
                dev_err(dev, "Error reading reset status: %pe\n", ERR_PTR(val));
                err = val;
                goto out;
        }

        /* Program the VCO group frequencies */
        for (i = 0; i < DS125DF111_NUM_VCO_GROUP_REG; i++) {
                err = i2c_smbus_write_byte_data(i2c,
                                                DS125DF111_VCO_GROUP_BASE + i,
                                                cfg->vco_group[i]);
                if (err < 0) {
                        dev_err(dev, "Error programming VCO group: %pe\n",
                                ERR_PTR(err));
                        goto out;
                }
        }

        /* Set the divide ratios for the VCO groups */
        ratios_val = FIELD_PREP(DS125DF111_RATIOS_RATE_MASK, cfg->rate) |
                FIELD_PREP(DS125DF111_RATIOS_SUBRATE_MASK, cfg->subrate);
        err = ds125df111_rmw(priv, DS125DF111_RATIOS, DS125DF111_RATIOS_MASK,
                             ratios_val);
        if (err < 0) {
                dev_err(dev, "Error programming the divide ratios: %pe\n",
                        ERR_PTR(err));
                goto out;
        }

out:
        mutex_unlock(&priv->mutex);

        return err;
}

static int ds125df111_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
        const struct ds125df111_config *cfg;

        if (mode != PHY_MODE_ETHERNET)
                return -EINVAL;

        switch (submode) {
        case PHY_INTERFACE_MODE_10GBASER:
                cfg = &ds125df111_cfg[FREQ_10G];
                break;
        case PHY_INTERFACE_MODE_1000BASEX:
        case PHY_INTERFACE_MODE_SGMII:
                cfg = &ds125df111_cfg[FREQ_1G];
                break;
        default:
                return -EINVAL;
        }

        return ds125df111_configure(phy, cfg);
}

static int ds125df111_validate(struct phy *phy, enum phy_mode mode, int submode,
                               union phy_configure_opts *opts __always_unused)
{
        if (mode != PHY_MODE_ETHERNET)
                return -EINVAL;

        switch (submode) {
        case PHY_INTERFACE_MODE_10GBASER:
        case PHY_INTERFACE_MODE_1000BASEX:
        case PHY_INTERFACE_MODE_SGMII:
                return 0;
        default:
                return -EINVAL;
        }
}

static const struct phy_ops ds125df111_ops = {
        .validate       = ds125df111_validate,
        .set_mode       = ds125df111_set_mode,
        .owner          = THIS_MODULE,
};

static struct phy *ds125df111_xlate(struct device *dev,
                                    const struct of_phandle_args *args)
{
        struct ds125df111_priv *priv = dev_get_drvdata(dev);
        u32 idx;

        if (args->args_count != 1)
                return ERR_PTR(-EINVAL);

        idx = args->args[0];
        if (idx >= DS125DF111_NUM_CH) {
                dev_err(dev, "Maximum number of channels is %d\n",
                        DS125DF111_NUM_CH);
                return ERR_PTR(-EINVAL);
        }

        return priv->ch[idx].phy;
}

static int ds125df111_probe(struct i2c_client *client)
{
        struct device *dev = &client->dev;
        struct phy_provider *provider;
        struct ds125df111_priv *priv;
        int i, err;

        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
        priv->client = client;
        err = devm_mutex_init(dev, &priv->mutex);
        if (err)
                return err;

        i2c_set_clientdata(client, priv);

        for (i = 0; i < DS125DF111_NUM_CH; i++) {
                struct ds125df111_ch *ch = &priv->ch[i];
                struct phy *phy;

                phy = devm_phy_create(dev, NULL, &ds125df111_ops);
                if (IS_ERR(phy))
                        return PTR_ERR(phy);

                ch->idx = i;
                ch->priv = priv;
                ch->phy = phy;

                phy_set_drvdata(phy, ch);
        }

        provider = devm_of_phy_provider_register(dev, ds125df111_xlate);

        return PTR_ERR_OR_ZERO(provider);
}

static const struct of_device_id ds125df111_dt_ids[] = {
        { .compatible = "ti,ds125df111", },
        {}
};
MODULE_DEVICE_TABLE(of, ds125df111_dt_ids);

static struct i2c_driver ds125df111_driver = {
        .driver = {
                .name = "ds125df111",
                .of_match_table = ds125df111_dt_ids,
        },
        .probe = ds125df111_probe,
};
module_i2c_driver(ds125df111_driver);

MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>");
MODULE_DESCRIPTION("TI DS125DF111 Retimer driver");
MODULE_LICENSE("GPL");