mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
Determine if probe of struct i2c_driver has i2c_device_id argument
or not.
This argument get removed from commit 03c835f498b5 ("i2c: Switch
.probe() to not take an id parameter") form Linux 6.3.
Bug 4346767
Change-Id: Ife73b29946246fce5bfcedcabe15992432d86348
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3030809
(cherry picked from commit 2884c4b380)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3054211
Tested-by: Jonathan Hunter <jonathanh@nvidia.com>
Reviewed-by: Jonathan Hunter <jonathanh@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
374 lines
9.7 KiB
C
374 lines
9.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Voltage Regulator Specification: VRS11 High Current Voltage Regulator
|
|
*
|
|
* Copyright (C) 2022-2023 NVIDIA CORPORATION. All rights reserved.
|
|
*/
|
|
|
|
#include <nvidia/conftest.h>
|
|
|
|
#include <linux/i2c.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/err.h>
|
|
#include <linux/types.h>
|
|
#include <linux/version.h>
|
|
|
|
#define VOLTAGE_OFFSET 200 // 0.2V
|
|
#define VOLTAGE_SCALE 5 // 5mV
|
|
|
|
/* Vendor ID */
|
|
#define NVVRS11_REG_VENDOR_ID 0x00
|
|
#define NVVRS11_REG_MODEL_REV 0x01
|
|
|
|
/* Voltage Output registers */
|
|
#define NVVRS11_REG_VOUT_A 0x30
|
|
#define NVVRS11_REG_VOUT_B 0x33
|
|
|
|
/* Current Output registers */
|
|
#define NVVRS11_REG_IOUT_A 0x31
|
|
#define NVVRS11_REG_IOUT_B 0x34
|
|
|
|
/* Temperature registers */
|
|
#define NVVRS11_REG_TEMP_A 0x32
|
|
#define NVVRS11_REG_TEMP_B 0x35
|
|
|
|
struct nvvrs11_chip {
|
|
struct device *dev;
|
|
struct regmap *rmap;
|
|
struct i2c_client *client;
|
|
const char *loopA_rail_name;
|
|
const char *loopB_rail_name;
|
|
};
|
|
|
|
static const struct regmap_range nvvrs11_readable_ranges[] = {
|
|
regmap_reg_range(NVVRS11_REG_VENDOR_ID, NVVRS11_REG_MODEL_REV),
|
|
regmap_reg_range(NVVRS11_REG_VOUT_A, NVVRS11_REG_TEMP_B),
|
|
};
|
|
|
|
static const struct regmap_access_table nvvrs11_readable_table = {
|
|
.yes_ranges = nvvrs11_readable_ranges,
|
|
.n_yes_ranges = ARRAY_SIZE(nvvrs11_readable_ranges),
|
|
};
|
|
|
|
static const struct regmap_config nvvrs11_regmap_config = {
|
|
.name = "nvvrs11",
|
|
.reg_bits = 8,
|
|
.val_bits = 8,
|
|
.max_register = NVVRS11_REG_TEMP_B + 1,
|
|
.cache_type = REGCACHE_RBTREE,
|
|
.rd_table = &nvvrs11_readable_table,
|
|
};
|
|
|
|
static ssize_t show_loopA_rail_name(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct nvvrs11_chip *nvvrs_chip = dev_get_drvdata(dev);
|
|
|
|
return sprintf(buf, "%s\n", nvvrs_chip->loopA_rail_name);
|
|
}
|
|
|
|
static ssize_t show_loopA_rail_voltage(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct nvvrs11_chip *nvvrs_chip = dev_get_drvdata(dev);
|
|
int voltage_A;
|
|
|
|
voltage_A = i2c_smbus_read_byte_data(nvvrs_chip->client, NVVRS11_REG_VOUT_A);
|
|
if (voltage_A < 0)
|
|
return voltage_A;
|
|
|
|
voltage_A *= VOLTAGE_SCALE;
|
|
voltage_A += VOLTAGE_OFFSET;
|
|
|
|
return sprintf(buf, "%u mV\n", voltage_A);
|
|
}
|
|
|
|
static ssize_t show_loopA_rail_current(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct nvvrs11_chip *nvvrs_chip = dev_get_drvdata(dev);
|
|
int current_A;
|
|
|
|
current_A = i2c_smbus_read_byte_data(nvvrs_chip->client, NVVRS11_REG_IOUT_A);
|
|
if (current_A < 0)
|
|
return current_A;
|
|
|
|
return sprintf(buf, "%u A\n", current_A);
|
|
}
|
|
|
|
static ssize_t show_loopA_rail_power(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct nvvrs11_chip *nvvrs_chip = dev_get_drvdata(dev);
|
|
unsigned int power;
|
|
int voltage_A;
|
|
int current_A;
|
|
|
|
voltage_A = i2c_smbus_read_byte_data(nvvrs_chip->client, NVVRS11_REG_VOUT_A);
|
|
if (voltage_A < 0)
|
|
return voltage_A;
|
|
|
|
voltage_A *= VOLTAGE_SCALE;
|
|
voltage_A += VOLTAGE_OFFSET;
|
|
|
|
current_A = i2c_smbus_read_byte_data(nvvrs_chip->client, NVVRS11_REG_IOUT_A);
|
|
if (current_A < 0)
|
|
return current_A;
|
|
|
|
power = (voltage_A * current_A)/1000;
|
|
return sprintf(buf, "%u W\n", power);
|
|
}
|
|
|
|
static ssize_t show_loopB_rail_name(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct nvvrs11_chip *nvvrs_chip = dev_get_drvdata(dev);
|
|
|
|
return sprintf(buf, "%s\n", nvvrs_chip->loopB_rail_name);
|
|
}
|
|
|
|
static ssize_t show_loopB_rail_voltage(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct nvvrs11_chip *nvvrs_chip = dev_get_drvdata(dev);
|
|
int voltage_B;
|
|
|
|
voltage_B = i2c_smbus_read_byte_data(nvvrs_chip->client, NVVRS11_REG_VOUT_B);
|
|
if (voltage_B < 0)
|
|
return voltage_B;
|
|
|
|
voltage_B *= VOLTAGE_SCALE;
|
|
voltage_B += VOLTAGE_OFFSET;
|
|
|
|
return sprintf(buf, "%u mV\n", voltage_B);
|
|
}
|
|
|
|
static ssize_t show_loopB_rail_current(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct nvvrs11_chip *nvvrs_chip = dev_get_drvdata(dev);
|
|
int current_B;
|
|
|
|
current_B = i2c_smbus_read_byte_data(nvvrs_chip->client, NVVRS11_REG_IOUT_B);
|
|
if (current_B < 0)
|
|
return current_B;
|
|
|
|
return sprintf(buf, "%u A\n", current_B);
|
|
}
|
|
|
|
static ssize_t show_loopB_rail_power(struct device *dev,
|
|
struct device_attribute *devattr, char *buf)
|
|
{
|
|
struct nvvrs11_chip *nvvrs_chip = dev_get_drvdata(dev);
|
|
unsigned int power;
|
|
int voltage_B;
|
|
int current_B;
|
|
|
|
voltage_B = i2c_smbus_read_byte_data(nvvrs_chip->client, NVVRS11_REG_VOUT_B);
|
|
if (voltage_B < 0)
|
|
return voltage_B;
|
|
|
|
voltage_B *= VOLTAGE_SCALE;
|
|
voltage_B += VOLTAGE_OFFSET;
|
|
|
|
current_B = i2c_smbus_read_byte_data(nvvrs_chip->client, NVVRS11_REG_IOUT_B);
|
|
if (current_B < 0)
|
|
return current_B;
|
|
|
|
power = (voltage_B * current_B)/1000;
|
|
|
|
return sprintf(buf, "%u W\n", power);
|
|
}
|
|
|
|
static DEVICE_ATTR(loopA_rail_name, S_IRUGO, show_loopA_rail_name, NULL);
|
|
static DEVICE_ATTR(loopA_rail_voltage, S_IRUGO, show_loopA_rail_voltage, NULL);
|
|
static DEVICE_ATTR(loopA_rail_current, S_IRUGO, show_loopA_rail_current, NULL);
|
|
static DEVICE_ATTR(loopA_rail_power, S_IRUGO, show_loopA_rail_power, NULL);
|
|
static DEVICE_ATTR(loopB_rail_name, S_IRUGO, show_loopB_rail_name, NULL);
|
|
static DEVICE_ATTR(loopB_rail_voltage, S_IRUGO, show_loopB_rail_voltage, NULL);
|
|
static DEVICE_ATTR(loopB_rail_current, S_IRUGO, show_loopB_rail_current, NULL);
|
|
static DEVICE_ATTR(loopB_rail_power, S_IRUGO, show_loopB_rail_power, NULL);
|
|
|
|
static struct attribute *nvvrs11_attr[] = {
|
|
&dev_attr_loopA_rail_name.attr,
|
|
&dev_attr_loopA_rail_voltage.attr,
|
|
&dev_attr_loopA_rail_current.attr,
|
|
&dev_attr_loopA_rail_power.attr,
|
|
&dev_attr_loopB_rail_name.attr,
|
|
&dev_attr_loopB_rail_voltage.attr,
|
|
&dev_attr_loopB_rail_current.attr,
|
|
&dev_attr_loopB_rail_power.attr,
|
|
NULL
|
|
};
|
|
|
|
static const struct attribute_group nvvrs11_attr_group = {
|
|
.attrs = nvvrs11_attr,
|
|
};
|
|
|
|
static int nvvrs11_create_sys_files(struct device *dev)
|
|
{
|
|
return sysfs_create_group(&dev->kobj, &nvvrs11_attr_group);
|
|
}
|
|
|
|
static void nvvrs11_delete_sys_files(struct device *dev)
|
|
{
|
|
return sysfs_remove_group(&dev->kobj, &nvvrs11_attr_group);
|
|
}
|
|
|
|
static int nvvrs11_vendor_info(struct nvvrs11_chip *chip)
|
|
{
|
|
struct i2c_client *client = chip->client;
|
|
int vendor_id, model_rev;
|
|
|
|
vendor_id = i2c_smbus_read_byte_data(client, NVVRS11_REG_VENDOR_ID);
|
|
if (vendor_id < 0) {
|
|
dev_err(chip->dev, "Failed to read Vendor ID: %d\n", vendor_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev_info(chip->dev, "NVVRS11 Vendor ID: 0x%X \n", vendor_id);
|
|
|
|
model_rev = i2c_smbus_read_byte_data(client, NVVRS11_REG_MODEL_REV);
|
|
if (model_rev < 0) {
|
|
dev_err(chip->dev, "Failed to read Model Rev: %d\n", model_rev);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dev_info(chip->dev, "NVVRS11 Model Rev: 0x%X\n", model_rev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(NV_I2C_DRIVER_STRUCT_PROBE_WITHOUT_I2C_DEVICE_ID_ARG) /* Linux 6.3 */
|
|
static int nvvrs11_probe(struct i2c_client *client)
|
|
#else
|
|
static int nvvrs11_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
#endif
|
|
{
|
|
const struct regmap_config *rmap_config;
|
|
struct nvvrs11_chip *nvvrs_chip;
|
|
struct device *dev = &client->dev;
|
|
struct device_node *np = dev->of_node;
|
|
int ret;
|
|
|
|
nvvrs_chip = devm_kzalloc(&client->dev, sizeof(*nvvrs_chip), GFP_KERNEL);
|
|
if (!nvvrs_chip)
|
|
return -ENOMEM;
|
|
|
|
/* Set PEC flag for SMBUS transfer with PEC enabled */
|
|
client->flags |= I2C_CLIENT_PEC;
|
|
|
|
i2c_set_clientdata(client, nvvrs_chip);
|
|
nvvrs_chip->client = client;
|
|
nvvrs_chip->dev = &client->dev;
|
|
rmap_config = &nvvrs11_regmap_config;
|
|
|
|
nvvrs_chip->rmap = devm_regmap_init_i2c(client, rmap_config);
|
|
if (IS_ERR(nvvrs_chip->rmap)) {
|
|
ret = PTR_ERR(nvvrs_chip->rmap);
|
|
dev_err(nvvrs_chip->dev, "Failed to initialise regmap: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/* Read device tree node info */
|
|
nvvrs_chip->loopA_rail_name = of_get_property(np, "rail-name-loopA", NULL);
|
|
if (!(nvvrs_chip->loopA_rail_name)) {
|
|
dev_info(nvvrs_chip->dev, "loopA rail does not exist\n");
|
|
nvvrs_chip->loopA_rail_name = "LoopA";
|
|
}
|
|
|
|
nvvrs_chip->loopB_rail_name = of_get_property(np, "rail-name-loopB", NULL);
|
|
if (!(nvvrs_chip->loopB_rail_name)) {
|
|
dev_info(nvvrs_chip->dev, "loopB rail does not exist\n");
|
|
nvvrs_chip->loopB_rail_name = "LoopB";
|
|
}
|
|
|
|
ret = nvvrs11_create_sys_files(nvvrs_chip->dev);
|
|
if (ret) {
|
|
dev_err(nvvrs_chip->dev, "Failed to add sysfs entries: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = nvvrs11_vendor_info(nvvrs_chip);
|
|
if (ret < 0) {
|
|
dev_err(nvvrs_chip->dev, "Failed to read vendor info: %d\n", ret);
|
|
goto exit;
|
|
}
|
|
|
|
dev_info(nvvrs_chip->dev, "NVVRS11 probe successful");
|
|
return 0;
|
|
|
|
exit:
|
|
nvvrs11_delete_sys_files(nvvrs_chip->dev);
|
|
dev_info(nvvrs_chip->dev, "NVVRS11 probe failed");
|
|
return ret;
|
|
}
|
|
|
|
#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE
|
|
static void nvvrs11_remove(struct i2c_client *client)
|
|
{
|
|
nvvrs11_delete_sys_files(&client->dev);
|
|
}
|
|
#else
|
|
static int nvvrs11_remove(struct i2c_client *client)
|
|
{
|
|
nvvrs11_delete_sys_files(&client->dev);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int nvvrs11_i2c_suspend(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int nvvrs11_i2c_resume(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static const struct dev_pm_ops nvvrs11_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(nvvrs11_i2c_suspend, nvvrs11_i2c_resume)
|
|
};
|
|
|
|
static const struct of_device_id nvvrs_dt_match[] = {
|
|
{ .compatible = "nvidia,vrs11" },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, nvvrs_dt_match);
|
|
|
|
static struct i2c_driver nvvrs11_driver = {
|
|
.driver = {
|
|
.name = "nvvrs11",
|
|
.pm = &nvvrs11_pm_ops,
|
|
.of_match_table = of_match_ptr(nvvrs_dt_match),
|
|
},
|
|
.probe = nvvrs11_probe,
|
|
.remove = nvvrs11_remove,
|
|
};
|
|
|
|
static int __init nvvrs11_init(void)
|
|
{
|
|
return i2c_add_driver(&nvvrs11_driver);
|
|
}
|
|
module_init(nvvrs11_init);
|
|
|
|
static void __exit nvvrs11_exit(void)
|
|
{
|
|
i2c_del_driver(&nvvrs11_driver);
|
|
}
|
|
module_exit(nvvrs11_exit);
|
|
|
|
MODULE_DESCRIPTION("Nvidia VRS11: High Current Voltage Regulator Spec");
|
|
MODULE_AUTHOR("Shubhi Garg <shgarg@nvidia.com>");
|
|
MODULE_ALIAS("i2c:nvvrs11");
|
|
MODULE_LICENSE("GPL v2");
|