unplugged-kernel/drivers/misc/mediatek/pmic/mt6370/mt6370_pmu_i2c.c

382 lines
9.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/pm.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include "inc/mt6370_pmu.h"
#define MT6370_PMU_I2C_DRV_VERSION "1.0.3_MTK"
static bool dbg_log_en; /* module param to enable/disable debug log */
module_param(dbg_log_en, bool, 0644);
static int mt6370_pmu_read_device(void *i2c, u32 addr, int len, void *dst)
{
return i2c_smbus_read_i2c_block_data(i2c, addr, len, dst);
}
static int mt6370_pmu_write_device(void *i2c, u32 addr, int len,
const void *src)
{
return i2c_smbus_write_i2c_block_data(i2c, addr, len, src);
}
static struct rt_regmap_fops mt6370_regmap_fops = {
.read_device = mt6370_pmu_read_device,
.write_device = mt6370_pmu_write_device,
};
int mt6370_pmu_reg_read(struct mt6370_pmu_chip *chip, u8 addr)
{
#ifdef CONFIG_RT_REGMAP
struct rt_reg_data rrd = {0};
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x\n", __func__, addr);
mutex_lock(&chip->io_lock);
ret = rt_regmap_reg_read(chip->rd, &rrd, addr);
mutex_unlock(&chip->io_lock);
return (ret < 0 ? ret : rrd.rt_data.data_u32);
#else
u8 data = 0;
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x\n", __func__, addr);
mutex_lock(&chip->io_lock);
ret = mt6370_pmu_read_device(chip->i2c, addr, 1, &data);
mutex_unlock(&chip->io_lock);
return (ret < 0 ? ret : data);
#endif /* #ifdef CONFIG_RT_REGMAP */
}
EXPORT_SYMBOL(mt6370_pmu_reg_read);
int mt6370_pmu_reg_write(struct mt6370_pmu_chip *chip, u8 addr, u8 data)
{
#ifdef CONFIG_RT_REGMAP
struct rt_reg_data rrd = {0};
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x data %02x\n", __func__,
addr, data);
mutex_lock(&chip->io_lock);
ret = rt_regmap_reg_write(chip->rd, &rrd, addr, data);
mutex_unlock(&chip->io_lock);
return ret;
#else
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x data %02x\n", __func__,
addr, data);
mutex_lock(&chip->io_lock);
ret = mt6370_pmu_write_device(chip->i2c, addr, 1, &data);
mutex_unlock(&chip->io_lock);
return (ret < 0 ? ret : data);
#endif /* #ifdef CONFIG_RT_REGMAP */
}
EXPORT_SYMBOL(mt6370_pmu_reg_write);
int mt6370_pmu_reg_update_bits(struct mt6370_pmu_chip *chip, u8 addr,
u8 mask, u8 data)
{
#ifdef CONFIG_RT_REGMAP
struct rt_reg_data rrd = {0};
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x data %02x\n", __func__,
addr, data);
mt_dbg(chip->dev, "%s: mask %02x\n", __func__, mask);
mutex_lock(&chip->io_lock);
ret = rt_regmap_update_bits(chip->rd, &rrd, addr, mask, data);
mutex_unlock(&chip->io_lock);
return ret;
#else
u8 orig = 0;
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x data %02x\n", __func__,
addr, data);
mt_dbg(chip->dev, "%s: mask %02x\n", __func__, mask);
mutex_lock(&chip->io_lock);
ret = mt6370_pmu_read_device(chip->i2c, addr, 1, &orig);
if (ret < 0)
goto out_update_bits;
orig &= ~mask;
orig |= (data & mask);
ret = mt6370_pmu_write_device(chip->i2c, addr, 1, &orig);
out_update_bits:
mutex_unlock(&chip->io_lock);
return ret;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
EXPORT_SYMBOL(mt6370_pmu_reg_update_bits);
int mt6370_pmu_reg_block_read(struct mt6370_pmu_chip *chip, u8 addr,
int len, u8 *dest)
{
#ifdef CONFIG_RT_REGMAP
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x size %d\n", __func__,
addr, len);
mutex_lock(&chip->io_lock);
ret = rt_regmap_block_read(chip->rd, addr, len, dest);
mutex_unlock(&chip->io_lock);
return ret;
#else
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x size %d\n", __func__,
addr, len);
mutex_lock(&chip->io_lock);
ret = mt6370_pmu_read_device(chip->i2c, addr, len, dest);
mutex_unlock(&chip->io_lock);
return ret;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
EXPORT_SYMBOL(mt6370_pmu_reg_block_read);
int mt6370_pmu_reg_block_write(struct mt6370_pmu_chip *chip, u8 addr,
int len, const u8 *src)
{
#ifdef CONFIG_RT_REGMAP
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x size %d\n", __func__, addr,
len);
mutex_lock(&chip->io_lock);
ret = rt_regmap_block_write(chip->rd, addr, len, src);
mutex_unlock(&chip->io_lock);
return ret;
#else
int ret = 0;
mt_dbg(chip->dev, "%s: reg %02x size %d\n", __func__, addr,
len);
mutex_lock(&chip->io_lock);
ret = mt6370_pmu_write_device(chip->i2c, addr, len, src);
mutex_unlock(&chip->io_lock);
return ret;
#endif /* #ifdef CONFIG_RT_REGMAP */
}
EXPORT_SYMBOL(mt6370_pmu_reg_block_write);
static int mt_parse_dt(struct device *dev,
struct mt6370_pmu_platform_data *pdata)
{
struct device_node *np = dev->of_node;
int ret = 0;
#if (!defined(CONFIG_MTK_GPIO) || defined(CONFIG_MTK_GPIOLIB_STAND))
ret = of_get_named_gpio(np, "mt6370,intr_gpio", 0);
if (ret < 0)
goto out_parse_dt;
pdata->intr_gpio = ret;
#else
ret = of_property_read_u32(np, "mt6370,intr_gpio_num",
&pdata->intr_gpio);
if (ret < 0)
goto out_parse_dt;
#endif
return 0;
out_parse_dt:
return ret;
}
static inline void rt_config_of_node(struct device *dev)
{
struct device_node *np = NULL;
np = of_find_node_by_name(NULL, "mt6370_pmu_dts");
if (np) {
dev_notice(dev, "find mt6370_pmu_dts node\n");
dev->of_node = np;
}
}
static inline int mt6370_pmu_chip_id_check(struct i2c_client *i2c)
{
int ret = 0;
u8 vendor_id = 0, chip_rev = 0;
ret = i2c_smbus_read_byte_data(i2c, MT6370_PMU_REG_DEVINFO);
if (ret < 0)
return ret;
vendor_id = ret & 0xF0;
chip_rev = ret & 0x0F;
switch (vendor_id) {
case RT5081_VENDOR_ID:
case MT6370_VENDOR_ID:
case MT6371_VENDOR_ID:
case MT6372_VENDOR_ID:
case MT6372C_VENDOR_ID:
dev_notice(&i2c->dev, "%s: E%d(0x%02X)\n",
__func__, chip_rev, vendor_id);
break;
default:
dev_notice(&i2c->dev, "%s: vendor id(0x%02X) does not match\n",
__func__, vendor_id);
ret = -ENODEV;
}
return ret;
}
static int mt6370_pmu_suspend(struct device *dev)
{
struct mt6370_pmu_chip *chip = dev_get_drvdata(dev);
mt_dbg(chip->dev, "%s\n", __func__);
mt6370_pmu_irq_suspend(chip);
disable_irq(chip->irq);
return 0;
}
static int mt6370_pmu_resume(struct device *dev)
{
struct mt6370_pmu_chip *chip = dev_get_drvdata(dev);
mt_dbg(dev, "%s\n", __func__);
enable_irq(chip->irq);
mt6370_pmu_irq_resume(chip);
return 0;
}
static SIMPLE_DEV_PM_OPS(mt6370_pmu_pm_ops, mt6370_pmu_suspend,
mt6370_pmu_resume);
static int mt6370_pmu_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct mt6370_pmu_chip *chip;
struct mt6370_pmu_platform_data *pdata = dev_get_platdata(&i2c->dev);
bool use_dt = i2c->dev.of_node;
u8 dev_info = 0;
int ret = 0;
pr_info("%s: (%s)\n", __func__, MT6370_PMU_I2C_DRV_VERSION);
ret = mt6370_pmu_chip_id_check(i2c);
if (ret < 0)
return ret;
dev_info = ret;
if (use_dt) {
rt_config_of_node(&i2c->dev);
pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
ret = mt_parse_dt(&i2c->dev, pdata);
if (ret < 0) {
dev_err(&i2c->dev, "error parse platform data\n");
devm_kfree(&i2c->dev, pdata);
return ret;
}
i2c->dev.platform_data = pdata;
} else {
if (!pdata)
return -EINVAL;
}
chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->i2c = i2c;
chip->dev = &i2c->dev;
chip->chip_vid = dev_info & 0xF0;
chip->chip_rev = dev_info & 0x0F;
mutex_init(&chip->io_lock);
i2c_set_clientdata(i2c, chip);
pm_runtime_set_active(&i2c->dev);
ret = mt6370_pmu_regmap_register(chip, &mt6370_regmap_fops);
if (ret < 0)
goto out_regmap;
ret = mt6370_pmu_irq_register(chip);
if (ret < 0)
goto out_irq;
ret = mt6370_pmu_subdevs_register(chip);
if (ret < 0)
goto out_subdevs;
pm_runtime_enable(&i2c->dev);
dev_info(&i2c->dev, "%s successfully\n", __func__);
return 0;
out_subdevs:
mt6370_pmu_irq_unregister(chip);
out_irq:
mt6370_pmu_regmap_unregister(chip);
out_regmap:
pm_runtime_set_suspended(&i2c->dev);
devm_kfree(&i2c->dev, chip);
if (use_dt)
devm_kfree(&i2c->dev, pdata);
return ret;
}
static int mt6370_pmu_remove(struct i2c_client *i2c)
{
struct mt6370_pmu_chip *chip = i2c_get_clientdata(i2c);
pm_runtime_disable(&i2c->dev);
mt6370_pmu_subdevs_unregister(chip);
mt6370_pmu_irq_unregister(chip);
mt6370_pmu_regmap_unregister(chip);
pm_runtime_set_suspended(&i2c->dev);
dev_info(chip->dev, "%s successfully\n", __func__);
return 0;
}
static const struct i2c_device_id mt6370_pmu_id_table[] = {
{"mt6370_pmu", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, mt6370_pmu_id_table);
static const struct of_device_id mt6370_pmu_ofid_table[] = {
{.compatible = "mediatek,mt6370_pmu",},
{.compatible = "mediatek,subpmic_pmu",},
{},
};
MODULE_DEVICE_TABLE(of, mt6370_pmu_ofid_table);
static struct i2c_driver mt6370_pmu = {
.driver = {
.name = "mt6370_pmu",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mt6370_pmu_ofid_table),
.pm = &mt6370_pmu_pm_ops,
},
.probe = mt6370_pmu_probe,
.remove = mt6370_pmu_remove,
.id_table = mt6370_pmu_id_table,
};
module_i2c_driver(mt6370_pmu);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MediaTek MT6370 PMU");
MODULE_VERSION(MT6370_PMU_I2C_DRV_VERSION);
/*
* Release Note
* 1.0.3_MTK
* (1) disable_irq()/enable_irq() in suspend()/resume()
*
* 1.0.2_MTK
* (1) Add support for MT6372
* 1.0.1_MTK
* (1) Replace rt_mutex with mutex
*
* 1.0.0_MTK
* (1) Initial Release
*/