From cf15a1f3df1e3e2818c3711b8d5991b58444daa0 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Mon, 3 Apr 2023 10:43:31 +0000 Subject: [PATCH] i2c: buses: nvvrs11: Copy driver from kernel/nvidia Make the copy of drivers of i2c/busses/i2c-nvvrs11 from kernel/nvidia to kernel/nvidia-oot. This will remove the dependency of the OOT drivers with kernel/nvidia-oot repo. Bug 4038415 Change-Id: I3353f2744915763c3e5f14164746fc540cd585dc Signed-off-by: Laxman Dewangan Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2881573 Tested-by: mobile promotions Reviewed-by: mobile promotions --- drivers/i2c/busses/i2c-nvvrs11.c | 347 +++++++++++++++++++++++++++++++ include/linux/i2c-nvvrs11.h | 37 ++++ kernel-src-files-copy-list.txt | 4 - 3 files changed, 384 insertions(+), 4 deletions(-) create mode 100644 drivers/i2c/busses/i2c-nvvrs11.c create mode 100644 include/linux/i2c-nvvrs11.h diff --git a/drivers/i2c/busses/i2c-nvvrs11.c b/drivers/i2c/busses/i2c-nvvrs11.c new file mode 100644 index 00000000..bdf30f6a --- /dev/null +++ b/drivers/i2c/busses/i2c-nvvrs11.c @@ -0,0 +1,347 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VOLTAGE_OFFSET 200 // 0.2V +#define VOLTAGE_SCALE 5 // 5mV + +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 KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE +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 "); +MODULE_ALIAS("i2c:nvvrs11"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/i2c-nvvrs11.h b/include/linux/i2c-nvvrs11.h new file mode 100644 index 00000000..a47ed74b --- /dev/null +++ b/include/linux/i2c-nvvrs11.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Defining registers address and its bit definitions of NVVRS11 + * + * Copyright (C) 2022-2023 NVIDIA CORPORATION. All rights reserved. + */ + +#ifndef _LINUX_I2C_NVVRS11_H_ +#define _LINUX_I2C_NVVRS11_H_ + +#include + +/* 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; +}; + +#endif /* _LINUX_I2C_NVVRS11_H_ */ diff --git a/kernel-src-files-copy-list.txt b/kernel-src-files-copy-list.txt index 42fefd14..267b319f 100644 --- a/kernel-src-files-copy-list.txt +++ b/kernel-src-files-copy-list.txt @@ -67,7 +67,3 @@ nvidia/drivers/video/tegra/dc/bridge/maxim_gmsl_dp_serializer.c drivers/video/te # Files/directory for TI FPDLink DP serializer nvidia/drivers/video/tegra/dc/bridge/ti_fpdlink_dp_serializer.c drivers/video/tegra/dc/bridge - -# Files/directory for NVIDIA I2C VRS11 -nvidia/drivers/i2c/busses/i2c-nvvrs11.c drivers/i2c/busses/i2c-nvvrs11.c -nvidia/include/linux/i2c-nvvrs11.h include/linux/i2c-nvvrs11.h