Merge: Fix power logic to improve DGPU performance on a desktop system that doesn't report having a power supply

MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/2811

## BUGZILLA
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2222462

## UPSTREAM STATUS
Upstream Status: Patches have been accepted on kernel/git/torvalds/linux.git

## CONFLICTS
Minor conflict on patch 0007.

## BUILD INFORMATION
Build Info: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=53911520

## TESTING
Functional testing: OtherQA

## DESCRIPTION
This series ensures that dGPU cards run at maximum performance, by enabling downstream drivers to check power supplies accordingly.

Signed-off-by: Desnes Nunes <desnesn@redhat.com>

Approved-by: Tony Camuso <tcamuso@redhat.com>
Approved-by: Lenny Szubowicz <lszubowi@redhat.com>

Signed-off-by: Jan Stancek <jstancek@redhat.com>
This commit is contained in:
Jan Stancek 2023-08-18 16:21:29 +02:00
commit 3eea6be070
7 changed files with 107 additions and 66 deletions

View File

@ -9,6 +9,13 @@ menu "I2C Hardware Bus support"
comment "PC SMBus host controller drivers" comment "PC SMBus host controller drivers"
depends on PCI depends on PCI
config I2C_CCGX_UCSI
tristate
help
A common module to provide an API to instantiate UCSI device
for Cypress CCGx Type-C controller. Individual bus drivers
need to select this one on demand.
config I2C_ALI1535 config I2C_ALI1535
tristate "ALI 1535" tristate "ALI 1535"
depends on PCI depends on PCI
@ -247,6 +254,7 @@ config I2C_NFORCE2_S4985
config I2C_NVIDIA_GPU config I2C_NVIDIA_GPU
tristate "NVIDIA GPU I2C controller" tristate "NVIDIA GPU I2C controller"
depends on PCI depends on PCI
select I2C_CCGX_UCSI
help help
If you say yes to this option, support will be included for the If you say yes to this option, support will be included for the
NVIDIA GPU I2C controller which is used to communicate with the GPU's NVIDIA GPU I2C controller which is used to communicate with the GPU's
@ -572,6 +580,7 @@ config I2C_DESIGNWARE_PCI
tristate "Synopsys DesignWare PCI" tristate "Synopsys DesignWare PCI"
depends on PCI depends on PCI
select I2C_DESIGNWARE_CORE select I2C_DESIGNWARE_CORE
select I2C_CCGX_UCSI
help help
If you say yes to this option, support will be included for the If you say yes to this option, support will be included for the
Synopsys DesignWare I2C adapter. Only master mode is supported. Synopsys DesignWare I2C adapter. Only master mode is supported.

View File

@ -6,6 +6,9 @@
# ACPI drivers # ACPI drivers
obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o
# Auxiliary I2C/SMBus modules
obj-$(CONFIG_I2C_CCGX_UCSI) += i2c-ccgx-ucsi.o
# PC SMBus host controller drivers # PC SMBus host controller drivers
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Instantiate UCSI device for Cypress CCGx Type-C controller.
* Derived from i2c-designware-pcidrv.c and i2c-nvidia-gpu.c.
*/
#include <linux/i2c.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/string.h>
#include "i2c-ccgx-ucsi.h"
struct software_node;
struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq,
const struct software_node *swnode)
{
struct i2c_board_info info = {};
strscpy(info.type, "ccgx-ucsi", sizeof(info.type));
info.addr = 0x08;
info.irq = irq;
info.swnode = swnode;
return i2c_new_client_device(adapter, &info);
}
EXPORT_SYMBOL_GPL(i2c_new_ccgx_ucsi);
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __I2C_CCGX_UCSI_H_
#define __I2C_CCGX_UCSI_H_
struct i2c_adapter;
struct i2c_client;
struct software_node;
struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq,
const struct software_node *swnode);
#endif /* __I2C_CCGX_UCSI_H_ */

View File

@ -20,10 +20,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/power_supply.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "i2c-designware-core.h" #include "i2c-designware-core.h"
#include "i2c-ccgx-ucsi.h"
#define DRIVER_NAME "i2c-designware-pci" #define DRIVER_NAME "i2c-designware-pci"
#define AMD_CLK_RATE_HZ 100000 #define AMD_CLK_RATE_HZ 100000
@ -118,26 +120,6 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
return -ENODEV; return -ENODEV;
} }
/*
* TODO find a better way how to deduplicate instantiation
* of USB PD slave device from nVidia GPU driver.
*/
static int navi_amd_register_client(struct dw_i2c_dev *dev)
{
struct i2c_board_info info;
memset(&info, 0, sizeof(struct i2c_board_info));
strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE);
info.addr = 0x08;
info.irq = dev->irq;
dev->slave = i2c_new_client_device(&dev->adapter, &info);
if (IS_ERR(dev->slave))
return PTR_ERR(dev->slave);
return 0;
}
static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c) static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
{ {
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev); struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
@ -232,6 +214,16 @@ static int i2c_dw_pci_resume(struct device *dev)
static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend, static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend,
i2c_dw_pci_resume, NULL); i2c_dw_pci_resume, NULL);
static const struct property_entry dgpu_properties[] = {
/* USB-C doesn't power the system */
PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE),
{}
};
static const struct software_node dgpu_node = {
.properties = dgpu_properties,
};
static int i2c_dw_pci_probe(struct pci_dev *pdev, static int i2c_dw_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id) const struct pci_device_id *id)
{ {
@ -324,11 +316,10 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
} }
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) { if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
r = navi_amd_register_client(dev); dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, &dgpu_node);
if (r) { if (IS_ERR(dev->slave))
dev_err(dev->dev, "register client failed with %d\n", r); return dev_err_probe(dev->dev, PTR_ERR(dev->slave),
return r; "register UCSI failed\n");
}
} }
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);

View File

@ -14,9 +14,12 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/power_supply.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "i2c-ccgx-ucsi.h"
/* I2C definitions */ /* I2C definitions */
#define I2C_MST_CNTL 0x00 #define I2C_MST_CNTL 0x00
#define I2C_MST_CNTL_GEN_START BIT(0) #define I2C_MST_CNTL_GEN_START BIT(0)
@ -259,6 +262,8 @@ MODULE_DEVICE_TABLE(pci, gpu_i2c_ids);
static const struct property_entry ccgx_props[] = { static const struct property_entry ccgx_props[] = {
/* Use FW built for NVIDIA GPU only */ /* Use FW built for NVIDIA GPU only */
PROPERTY_ENTRY_STRING("firmware-name", "nvidia,gpu"), PROPERTY_ENTRY_STRING("firmware-name", "nvidia,gpu"),
/* USB-C doesn't power the system */
PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE),
{ } { }
}; };
@ -266,54 +271,32 @@ static const struct software_node ccgx_node = {
.properties = ccgx_props, .properties = ccgx_props,
}; };
static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq)
{
i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev,
sizeof(*i2cd->gpu_ccgx_ucsi),
GFP_KERNEL);
if (!i2cd->gpu_ccgx_ucsi)
return -ENOMEM;
strlcpy(i2cd->gpu_ccgx_ucsi->type, "ccgx-ucsi",
sizeof(i2cd->gpu_ccgx_ucsi->type));
i2cd->gpu_ccgx_ucsi->addr = 0x8;
i2cd->gpu_ccgx_ucsi->irq = irq;
i2cd->gpu_ccgx_ucsi->swnode = &ccgx_node;
i2cd->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
return PTR_ERR_OR_ZERO(i2cd->ccgx_client);
}
static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{ {
struct device *dev = &pdev->dev;
struct gpu_i2c_dev *i2cd; struct gpu_i2c_dev *i2cd;
int status; int status;
i2cd = devm_kzalloc(&pdev->dev, sizeof(*i2cd), GFP_KERNEL); i2cd = devm_kzalloc(dev, sizeof(*i2cd), GFP_KERNEL);
if (!i2cd) if (!i2cd)
return -ENOMEM; return -ENOMEM;
i2cd->dev = &pdev->dev; i2cd->dev = dev;
dev_set_drvdata(&pdev->dev, i2cd); dev_set_drvdata(dev, i2cd);
status = pcim_enable_device(pdev); status = pcim_enable_device(pdev);
if (status < 0) { if (status < 0)
dev_err(&pdev->dev, "pcim_enable_device failed %d\n", status); return dev_err_probe(dev, status, "pcim_enable_device failed\n");
return status;
}
pci_set_master(pdev); pci_set_master(pdev);
i2cd->regs = pcim_iomap(pdev, 0, 0); i2cd->regs = pcim_iomap(pdev, 0, 0);
if (!i2cd->regs) { if (!i2cd->regs)
dev_err(&pdev->dev, "pcim_iomap failed\n"); return dev_err_probe(dev, -ENOMEM, "pcim_iomap failed\n");
return -ENOMEM;
}
status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
if (status < 0) { if (status < 0)
dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", status); return dev_err_probe(dev, status, "pci_alloc_irq_vectors err\n");
return status;
}
gpu_enable_i2c_bus(i2cd); gpu_enable_i2c_bus(i2cd);
@ -323,21 +306,21 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
sizeof(i2cd->adapter.name)); sizeof(i2cd->adapter.name));
i2cd->adapter.algo = &gpu_i2c_algorithm; i2cd->adapter.algo = &gpu_i2c_algorithm;
i2cd->adapter.quirks = &gpu_i2c_quirks; i2cd->adapter.quirks = &gpu_i2c_quirks;
i2cd->adapter.dev.parent = &pdev->dev; i2cd->adapter.dev.parent = dev;
status = i2c_add_adapter(&i2cd->adapter); status = i2c_add_adapter(&i2cd->adapter);
if (status < 0) if (status < 0)
goto free_irq_vectors; goto free_irq_vectors;
status = gpu_populate_client(i2cd, pdev->irq); i2cd->ccgx_client = i2c_new_ccgx_ucsi(&i2cd->adapter, pdev->irq, &ccgx_node);
if (status < 0) { if (IS_ERR(i2cd->ccgx_client)) {
dev_err(&pdev->dev, "gpu_populate_client failed %d\n", status); status = dev_err_probe(dev, PTR_ERR(i2cd->ccgx_client), "register UCSI failed\n");
goto del_adapter; goto del_adapter;
} }
pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); pm_runtime_set_autosuspend_delay(dev, 3000);
pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_use_autosuspend(dev);
pm_runtime_put_autosuspend(&pdev->dev); pm_runtime_put_autosuspend(dev);
pm_runtime_allow(&pdev->dev); pm_runtime_allow(dev);
return 0; return 0;
@ -350,7 +333,7 @@ free_irq_vectors:
static void gpu_i2c_remove(struct pci_dev *pdev) static void gpu_i2c_remove(struct pci_dev *pdev)
{ {
struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); struct gpu_i2c_dev *i2cd = pci_get_drvdata(pdev);
pm_runtime_get_noresume(i2cd->dev); pm_runtime_get_noresume(i2cd->dev);
i2c_del_adapter(&i2cd->adapter); i2c_del_adapter(&i2cd->adapter);

View File

@ -27,8 +27,20 @@ static enum power_supply_property ucsi_psy_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_SCOPE,
}; };
static int ucsi_psy_get_scope(struct ucsi_connector *con,
union power_supply_propval *val)
{
u8 scope = POWER_SUPPLY_SCOPE_UNKNOWN;
struct device *dev = con->ucsi->dev;
device_property_read_u8(dev, "scope", &scope);
val->intval = scope;
return 0;
}
static int ucsi_psy_get_online(struct ucsi_connector *con, static int ucsi_psy_get_online(struct ucsi_connector *con,
union power_supply_propval *val) union power_supply_propval *val)
{ {
@ -194,6 +206,8 @@ static int ucsi_psy_get_prop(struct power_supply *psy,
return ucsi_psy_get_current_max(con, val); return ucsi_psy_get_current_max(con, val);
case POWER_SUPPLY_PROP_CURRENT_NOW: case POWER_SUPPLY_PROP_CURRENT_NOW:
return ucsi_psy_get_current_now(con, val); return ucsi_psy_get_current_now(con, val);
case POWER_SUPPLY_PROP_SCOPE:
return ucsi_psy_get_scope(con, val);
default: default:
return -EINVAL; return -EINVAL;
} }