root/drivers/mfd/rk8xx-i2c.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Rockchip RK801/RK805/RK808/RK816/RK817/RK818 Core (I2C) driver
 *
 * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
 * Copyright (C) 2016 PHYTEC Messtechnik GmbH
 *
 * Author: Chris Zhong <zyw@rock-chips.com>
 * Author: Zhang Qing <zhangqing@rock-chips.com>
 * Author: Wadim Egorov <w.egorov@phytec.de>
 */

#include <linux/i2c.h>
#include <linux/mfd/rk808.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>

struct rk8xx_i2c_platform_data {
        const struct regmap_config *regmap_cfg;
        int variant;
};

static bool rk801_is_volatile_reg(struct device *dev, unsigned int reg)
{
        switch (reg) {
        case RK801_SYS_STS_REG:
        case RK801_INT_STS0_REG:
        case RK801_SYS_CFG0_REG:
        case RK801_SYS_CFG1_REG:
        case RK801_SYS_CFG2_REG:
        case RK801_SYS_CFG3_REG:
        case RK801_SYS_CFG4_REG:
        case RK801_SLEEP_CFG_REG:
                return true;
        }

        return false;
}

static bool rk806_is_volatile_reg(struct device *dev, unsigned int reg)
{
        switch (reg) {
        case RK806_POWER_EN0 ... RK806_POWER_EN5:
        case RK806_DVS_START_CTRL ... RK806_INT_MSK1:
                return true;
        }

        return false;
}

static bool rk808_is_volatile_reg(struct device *dev, unsigned int reg)
{
        /*
         * Notes:
         * - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but
         *   we don't use that feature.  It's better to cache.
         * - It's unlikely we care that RK808_DEVCTRL_REG is volatile since
         *   bits are cleared in case when we shutoff anyway, but better safe.
         */

        switch (reg) {
        case RK808_SECONDS_REG ... RK808_WEEKS_REG:
        case RK808_RTC_STATUS_REG:
        case RK808_VB_MON_REG:
        case RK808_THERMAL_REG:
        case RK808_DCDC_UV_STS_REG:
        case RK808_LDO_UV_STS_REG:
        case RK808_DCDC_PG_REG:
        case RK808_LDO_PG_REG:
        case RK808_DEVCTRL_REG:
        case RK808_INT_STS_REG1:
        case RK808_INT_STS_REG2:
                return true;
        }

        return false;
}

static bool rk816_is_volatile_reg(struct device *dev, unsigned int reg)
{
        /*
         * Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but
         * we don't use that feature.  It's better to cache.
         */

        switch (reg) {
        case RK808_SECONDS_REG ... RK808_WEEKS_REG:
        case RK808_RTC_STATUS_REG:
        case RK808_VB_MON_REG:
        case RK808_THERMAL_REG:
        case RK816_DCDC_EN_REG1:
        case RK816_DCDC_EN_REG2:
        case RK816_INT_STS_REG1:
        case RK816_INT_STS_REG2:
        case RK816_INT_STS_REG3:
        case RK808_DEVCTRL_REG:
        case RK816_SUP_STS_REG:
        case RK816_GGSTS_REG:
        case RK816_ZERO_CUR_ADC_REGH:
        case RK816_ZERO_CUR_ADC_REGL:
        case RK816_GASCNT_REG(0) ... RK816_BAT_VOL_REGL:
                return true;
        }

        return false;
}

static bool rk817_is_volatile_reg(struct device *dev, unsigned int reg)
{
        /*
         * Notes:
         * - Technically the ROUND_30s bit makes RTC_CTRL_REG volatile, but
         *   we don't use that feature.  It's better to cache.
         */

        switch (reg) {
        case RK817_SECONDS_REG ... RK817_WEEKS_REG:
        case RK817_RTC_STATUS_REG:
        case RK817_CODEC_DTOP_LPT_SRST:
        case RK817_GAS_GAUGE_ADC_CONFIG0 ... RK817_GAS_GAUGE_CUR_ADC_K0:
        case RK817_PMIC_CHRG_STS:
        case RK817_PMIC_CHRG_OUT:
        case RK817_PMIC_CHRG_IN:
        case RK817_INT_STS_REG0:
        case RK817_INT_STS_REG1:
        case RK817_INT_STS_REG2:
        case RK817_SYS_STS:
                return true;
        }

        return false;
}


static const struct regmap_config rk818_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .max_register = RK818_USB_CTRL_REG,
        .cache_type = REGCACHE_MAPLE,
        .volatile_reg = rk808_is_volatile_reg,
};

static const struct regmap_config rk801_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .max_register = RK801_SYS_CFG3_OTP_REG,
        .cache_type = REGCACHE_RBTREE,
        .volatile_reg = rk801_is_volatile_reg,
};

static const struct regmap_config rk805_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .max_register = RK805_OFF_SOURCE_REG,
        .cache_type = REGCACHE_MAPLE,
        .volatile_reg = rk808_is_volatile_reg,
};

static const struct regmap_config rk806_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .max_register = RK806_BUCK_RSERVE_REG5,
        .cache_type = REGCACHE_MAPLE,
        .volatile_reg = rk806_is_volatile_reg,
};

static const struct regmap_config rk808_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .max_register = RK808_IO_POL_REG,
        .cache_type = REGCACHE_MAPLE,
        .volatile_reg = rk808_is_volatile_reg,
};

static const struct regmap_config rk816_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .max_register = RK816_DATA_REG(18),
        .cache_type = REGCACHE_MAPLE,
        .volatile_reg = rk816_is_volatile_reg,
};

static const struct regmap_config rk817_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .max_register = RK817_GPIO_INT_CFG,
        .cache_type = REGCACHE_NONE,
        .volatile_reg = rk817_is_volatile_reg,
};

static const struct rk8xx_i2c_platform_data rk801_data = {
        .regmap_cfg = &rk801_regmap_config,
        .variant = RK801_ID,
};

static const struct rk8xx_i2c_platform_data rk805_data = {
        .regmap_cfg = &rk805_regmap_config,
        .variant = RK805_ID,
};

static const struct rk8xx_i2c_platform_data rk806_data = {
        .regmap_cfg = &rk806_regmap_config,
        .variant = RK806_ID,
};

static const struct rk8xx_i2c_platform_data rk808_data = {
        .regmap_cfg = &rk808_regmap_config,
        .variant = RK808_ID,
};

static const struct rk8xx_i2c_platform_data rk809_data = {
        .regmap_cfg = &rk817_regmap_config,
        .variant = RK809_ID,
};

static const struct rk8xx_i2c_platform_data rk816_data = {
        .regmap_cfg = &rk816_regmap_config,
        .variant = RK816_ID,
};

static const struct rk8xx_i2c_platform_data rk817_data = {
        .regmap_cfg = &rk817_regmap_config,
        .variant = RK817_ID,
};

static const struct rk8xx_i2c_platform_data rk818_data = {
        .regmap_cfg = &rk818_regmap_config,
        .variant = RK818_ID,
};

static int rk8xx_i2c_probe(struct i2c_client *client)
{
        const struct rk8xx_i2c_platform_data *data;
        struct regmap *regmap;

        data = device_get_match_data(&client->dev);
        if (!data)
                return -ENODEV;

        regmap = devm_regmap_init_i2c(client, data->regmap_cfg);
        if (IS_ERR(regmap))
                return dev_err_probe(&client->dev, PTR_ERR(regmap),
                                     "regmap initialization failed\n");

        return rk8xx_probe(&client->dev, data->variant, client->irq, regmap);
}

static void rk8xx_i2c_shutdown(struct i2c_client *client)
{
        rk8xx_shutdown(&client->dev);
}

static SIMPLE_DEV_PM_OPS(rk8xx_i2c_pm_ops, rk8xx_suspend, rk8xx_resume);

static const struct of_device_id rk8xx_i2c_of_match[] = {
        { .compatible = "rockchip,rk801", .data = &rk801_data },
        { .compatible = "rockchip,rk805", .data = &rk805_data },
        { .compatible = "rockchip,rk806", .data = &rk806_data },
        { .compatible = "rockchip,rk808", .data = &rk808_data },
        { .compatible = "rockchip,rk809", .data = &rk809_data },
        { .compatible = "rockchip,rk816", .data = &rk816_data },
        { .compatible = "rockchip,rk817", .data = &rk817_data },
        { .compatible = "rockchip,rk818", .data = &rk818_data },
        { },
};
MODULE_DEVICE_TABLE(of, rk8xx_i2c_of_match);

static struct i2c_driver rk8xx_i2c_driver = {
        .driver = {
                .name = "rk8xx-i2c",
                .of_match_table = rk8xx_i2c_of_match,
                .pm = &rk8xx_i2c_pm_ops,
        },
        .probe = rk8xx_i2c_probe,
        .shutdown  = rk8xx_i2c_shutdown,
};
module_i2c_driver(rk8xx_i2c_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
MODULE_AUTHOR("Wadim Egorov <w.egorov@phytec.de>");
MODULE_DESCRIPTION("RK8xx I2C PMIC driver");