18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Nvidia GPU I2C controller Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2018 NVIDIA Corporation. All rights reserved. 68c2ecf20Sopenharmony_ci * Author: Ajay Gupta <ajayg@nvidia.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/i2c.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/pci.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/pm.h> 168c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* I2C definitions */ 218c2ecf20Sopenharmony_ci#define I2C_MST_CNTL 0x00 228c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_GEN_START BIT(0) 238c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_GEN_STOP BIT(1) 248c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_CMD_READ (1 << 2) 258c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_CMD_WRITE (2 << 2) 268c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_BURST_SIZE_SHIFT 6 278c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_GEN_NACK BIT(28) 288c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_STATUS GENMASK(30, 29) 298c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_STATUS_OKAY (0 << 29) 308c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29) 318c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_STATUS_TIMEOUT (2 << 29) 328c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29) 338c2ecf20Sopenharmony_ci#define I2C_MST_CNTL_CYCLE_TRIGGER BIT(31) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define I2C_MST_ADDR 0x04 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define I2C_MST_I2C0_TIMING 0x08 388c2ecf20Sopenharmony_ci#define I2C_MST_I2C0_TIMING_SCL_PERIOD_100KHZ 0x10e 398c2ecf20Sopenharmony_ci#define I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT 16 408c2ecf20Sopenharmony_ci#define I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT_MAX 255 418c2ecf20Sopenharmony_ci#define I2C_MST_I2C0_TIMING_TIMEOUT_CHECK BIT(24) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define I2C_MST_DATA 0x0c 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define I2C_MST_HYBRID_PADCTL 0x20 468c2ecf20Sopenharmony_ci#define I2C_MST_HYBRID_PADCTL_MODE_I2C BIT(0) 478c2ecf20Sopenharmony_ci#define I2C_MST_HYBRID_PADCTL_I2C_SCL_INPUT_RCV BIT(14) 488c2ecf20Sopenharmony_ci#define I2C_MST_HYBRID_PADCTL_I2C_SDA_INPUT_RCV BIT(15) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct gpu_i2c_dev { 518c2ecf20Sopenharmony_ci struct device *dev; 528c2ecf20Sopenharmony_ci void __iomem *regs; 538c2ecf20Sopenharmony_ci struct i2c_adapter adapter; 548c2ecf20Sopenharmony_ci struct i2c_board_info *gpu_ccgx_ucsi; 558c2ecf20Sopenharmony_ci struct i2c_client *ccgx_client; 568c2ecf20Sopenharmony_ci}; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci u32 val; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* enable I2C */ 638c2ecf20Sopenharmony_ci val = readl(i2cd->regs + I2C_MST_HYBRID_PADCTL); 648c2ecf20Sopenharmony_ci val |= I2C_MST_HYBRID_PADCTL_MODE_I2C | 658c2ecf20Sopenharmony_ci I2C_MST_HYBRID_PADCTL_I2C_SCL_INPUT_RCV | 668c2ecf20Sopenharmony_ci I2C_MST_HYBRID_PADCTL_I2C_SDA_INPUT_RCV; 678c2ecf20Sopenharmony_ci writel(val, i2cd->regs + I2C_MST_HYBRID_PADCTL); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* enable 100KHZ mode */ 708c2ecf20Sopenharmony_ci val = I2C_MST_I2C0_TIMING_SCL_PERIOD_100KHZ; 718c2ecf20Sopenharmony_ci val |= (I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT_MAX 728c2ecf20Sopenharmony_ci << I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT); 738c2ecf20Sopenharmony_ci val |= I2C_MST_I2C0_TIMING_TIMEOUT_CHECK; 748c2ecf20Sopenharmony_ci writel(val, i2cd->regs + I2C_MST_I2C0_TIMING); 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int gpu_i2c_check_status(struct gpu_i2c_dev *i2cd) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci u32 val; 808c2ecf20Sopenharmony_ci int ret; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci ret = readl_poll_timeout(i2cd->regs + I2C_MST_CNTL, val, 838c2ecf20Sopenharmony_ci !(val & I2C_MST_CNTL_CYCLE_TRIGGER) || 848c2ecf20Sopenharmony_ci (val & I2C_MST_CNTL_STATUS) != I2C_MST_CNTL_STATUS_BUS_BUSY, 858c2ecf20Sopenharmony_ci 500, 1000 * USEC_PER_MSEC); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (ret) { 888c2ecf20Sopenharmony_ci dev_err(i2cd->dev, "i2c timeout error %x\n", val); 898c2ecf20Sopenharmony_ci return -ETIMEDOUT; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci val = readl(i2cd->regs + I2C_MST_CNTL); 938c2ecf20Sopenharmony_ci switch (val & I2C_MST_CNTL_STATUS) { 948c2ecf20Sopenharmony_ci case I2C_MST_CNTL_STATUS_OKAY: 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci case I2C_MST_CNTL_STATUS_NO_ACK: 978c2ecf20Sopenharmony_ci return -ENXIO; 988c2ecf20Sopenharmony_ci case I2C_MST_CNTL_STATUS_TIMEOUT: 998c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1008c2ecf20Sopenharmony_ci default: 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int gpu_i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16 len) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int status; 1088c2ecf20Sopenharmony_ci u32 val; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci val = I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_CMD_READ | 1118c2ecf20Sopenharmony_ci (len << I2C_MST_CNTL_BURST_SIZE_SHIFT) | 1128c2ecf20Sopenharmony_ci I2C_MST_CNTL_CYCLE_TRIGGER | I2C_MST_CNTL_GEN_NACK; 1138c2ecf20Sopenharmony_ci writel(val, i2cd->regs + I2C_MST_CNTL); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci status = gpu_i2c_check_status(i2cd); 1168c2ecf20Sopenharmony_ci if (status < 0) 1178c2ecf20Sopenharmony_ci return status; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci val = readl(i2cd->regs + I2C_MST_DATA); 1208c2ecf20Sopenharmony_ci switch (len) { 1218c2ecf20Sopenharmony_ci case 1: 1228c2ecf20Sopenharmony_ci data[0] = val; 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci case 2: 1258c2ecf20Sopenharmony_ci put_unaligned_be16(val, data); 1268c2ecf20Sopenharmony_ci break; 1278c2ecf20Sopenharmony_ci case 3: 1288c2ecf20Sopenharmony_ci put_unaligned_be24(val, data); 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci case 4: 1318c2ecf20Sopenharmony_ci put_unaligned_be32(val, data); 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci default: 1348c2ecf20Sopenharmony_ci break; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci return status; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic int gpu_i2c_start(struct gpu_i2c_dev *i2cd) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci writel(I2C_MST_CNTL_GEN_START, i2cd->regs + I2C_MST_CNTL); 1428c2ecf20Sopenharmony_ci return gpu_i2c_check_status(i2cd); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int gpu_i2c_stop(struct gpu_i2c_dev *i2cd) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci writel(I2C_MST_CNTL_GEN_STOP, i2cd->regs + I2C_MST_CNTL); 1488c2ecf20Sopenharmony_ci return gpu_i2c_check_status(i2cd); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int gpu_i2c_write(struct gpu_i2c_dev *i2cd, u8 data) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci u32 val; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci writel(data, i2cd->regs + I2C_MST_DATA); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci val = I2C_MST_CNTL_CMD_WRITE | (1 << I2C_MST_CNTL_BURST_SIZE_SHIFT); 1588c2ecf20Sopenharmony_ci writel(val, i2cd->regs + I2C_MST_CNTL); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return gpu_i2c_check_status(i2cd); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int gpu_i2c_master_xfer(struct i2c_adapter *adap, 1648c2ecf20Sopenharmony_ci struct i2c_msg *msgs, int num) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap); 1678c2ecf20Sopenharmony_ci int status, status2; 1688c2ecf20Sopenharmony_ci bool send_stop = true; 1698c2ecf20Sopenharmony_ci int i, j; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * The controller supports maximum 4 byte read due to known 1738c2ecf20Sopenharmony_ci * limitation of sending STOP after every read. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci pm_runtime_get_sync(i2cd->dev); 1768c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 1778c2ecf20Sopenharmony_ci if (msgs[i].flags & I2C_M_RD) { 1788c2ecf20Sopenharmony_ci /* program client address before starting read */ 1798c2ecf20Sopenharmony_ci writel(msgs[i].addr, i2cd->regs + I2C_MST_ADDR); 1808c2ecf20Sopenharmony_ci /* gpu_i2c_read has implicit start */ 1818c2ecf20Sopenharmony_ci status = gpu_i2c_read(i2cd, msgs[i].buf, msgs[i].len); 1828c2ecf20Sopenharmony_ci if (status < 0) 1838c2ecf20Sopenharmony_ci goto exit; 1848c2ecf20Sopenharmony_ci } else { 1858c2ecf20Sopenharmony_ci u8 addr = i2c_8bit_addr_from_msg(msgs + i); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci status = gpu_i2c_start(i2cd); 1888c2ecf20Sopenharmony_ci if (status < 0) { 1898c2ecf20Sopenharmony_ci if (i == 0) 1908c2ecf20Sopenharmony_ci send_stop = false; 1918c2ecf20Sopenharmony_ci goto exit; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci status = gpu_i2c_write(i2cd, addr); 1958c2ecf20Sopenharmony_ci if (status < 0) 1968c2ecf20Sopenharmony_ci goto exit; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci for (j = 0; j < msgs[i].len; j++) { 1998c2ecf20Sopenharmony_ci status = gpu_i2c_write(i2cd, msgs[i].buf[j]); 2008c2ecf20Sopenharmony_ci if (status < 0) 2018c2ecf20Sopenharmony_ci goto exit; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci send_stop = false; 2068c2ecf20Sopenharmony_ci status = gpu_i2c_stop(i2cd); 2078c2ecf20Sopenharmony_ci if (status < 0) 2088c2ecf20Sopenharmony_ci goto exit; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci status = i; 2118c2ecf20Sopenharmony_ciexit: 2128c2ecf20Sopenharmony_ci if (send_stop) { 2138c2ecf20Sopenharmony_ci status2 = gpu_i2c_stop(i2cd); 2148c2ecf20Sopenharmony_ci if (status2 < 0) 2158c2ecf20Sopenharmony_ci dev_err(i2cd->dev, "i2c stop failed %d\n", status2); 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(i2cd->dev); 2188c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(i2cd->dev); 2198c2ecf20Sopenharmony_ci return status; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const struct i2c_adapter_quirks gpu_i2c_quirks = { 2238c2ecf20Sopenharmony_ci .max_read_len = 4, 2248c2ecf20Sopenharmony_ci .max_comb_2nd_msg_len = 4, 2258c2ecf20Sopenharmony_ci .flags = I2C_AQ_COMB_WRITE_THEN_READ, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic u32 gpu_i2c_functionality(struct i2c_adapter *adap) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic const struct i2c_algorithm gpu_i2c_algorithm = { 2348c2ecf20Sopenharmony_ci .master_xfer = gpu_i2c_master_xfer, 2358c2ecf20Sopenharmony_ci .functionality = gpu_i2c_functionality, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* 2398c2ecf20Sopenharmony_ci * This driver is for Nvidia GPU cards with USB Type-C interface. 2408c2ecf20Sopenharmony_ci * We want to identify the cards using vendor ID and class code only 2418c2ecf20Sopenharmony_ci * to avoid dependency of adding product id for any new card which 2428c2ecf20Sopenharmony_ci * requires this driver. 2438c2ecf20Sopenharmony_ci * Currently there is no class code defined for UCSI device over PCI 2448c2ecf20Sopenharmony_ci * so using UNKNOWN class for now and it will be updated when UCSI 2458c2ecf20Sopenharmony_ci * over PCI gets a class code. 2468c2ecf20Sopenharmony_ci * There is no other NVIDIA cards with UNKNOWN class code. Even if the 2478c2ecf20Sopenharmony_ci * driver gets loaded for an undesired card then eventually i2c_read() 2488c2ecf20Sopenharmony_ci * (initiated from UCSI i2c_client) will timeout or UCSI commands will 2498c2ecf20Sopenharmony_ci * timeout. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci#define PCI_CLASS_SERIAL_UNKNOWN 0x0c80 2528c2ecf20Sopenharmony_cistatic const struct pci_device_id gpu_i2c_ids[] = { 2538c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, 2548c2ecf20Sopenharmony_ci PCI_CLASS_SERIAL_UNKNOWN << 8, 0xffffff00}, 2558c2ecf20Sopenharmony_ci { } 2568c2ecf20Sopenharmony_ci}; 2578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, gpu_i2c_ids); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic const struct property_entry ccgx_props[] = { 2608c2ecf20Sopenharmony_ci /* Use FW built for NVIDIA (nv) only */ 2618c2ecf20Sopenharmony_ci PROPERTY_ENTRY_U16("ccgx,firmware-build", ('n' << 8) | 'v'), 2628c2ecf20Sopenharmony_ci { } 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev, 2688c2ecf20Sopenharmony_ci sizeof(*i2cd->gpu_ccgx_ucsi), 2698c2ecf20Sopenharmony_ci GFP_KERNEL); 2708c2ecf20Sopenharmony_ci if (!i2cd->gpu_ccgx_ucsi) 2718c2ecf20Sopenharmony_ci return -ENOMEM; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci strlcpy(i2cd->gpu_ccgx_ucsi->type, "ccgx-ucsi", 2748c2ecf20Sopenharmony_ci sizeof(i2cd->gpu_ccgx_ucsi->type)); 2758c2ecf20Sopenharmony_ci i2cd->gpu_ccgx_ucsi->addr = 0x8; 2768c2ecf20Sopenharmony_ci i2cd->gpu_ccgx_ucsi->irq = irq; 2778c2ecf20Sopenharmony_ci i2cd->gpu_ccgx_ucsi->properties = ccgx_props; 2788c2ecf20Sopenharmony_ci i2cd->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); 2798c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(i2cd->ccgx_client); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct gpu_i2c_dev *i2cd; 2858c2ecf20Sopenharmony_ci int status; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci i2cd = devm_kzalloc(&pdev->dev, sizeof(*i2cd), GFP_KERNEL); 2888c2ecf20Sopenharmony_ci if (!i2cd) 2898c2ecf20Sopenharmony_ci return -ENOMEM; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci i2cd->dev = &pdev->dev; 2928c2ecf20Sopenharmony_ci dev_set_drvdata(&pdev->dev, i2cd); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci status = pcim_enable_device(pdev); 2958c2ecf20Sopenharmony_ci if (status < 0) { 2968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pcim_enable_device failed %d\n", status); 2978c2ecf20Sopenharmony_ci return status; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci pci_set_master(pdev); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci i2cd->regs = pcim_iomap(pdev, 0, 0); 3038c2ecf20Sopenharmony_ci if (!i2cd->regs) { 3048c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pcim_iomap failed\n"); 3058c2ecf20Sopenharmony_ci return -ENOMEM; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); 3098c2ecf20Sopenharmony_ci if (status < 0) { 3108c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", status); 3118c2ecf20Sopenharmony_ci return status; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci gpu_enable_i2c_bus(i2cd); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci i2c_set_adapdata(&i2cd->adapter, i2cd); 3178c2ecf20Sopenharmony_ci i2cd->adapter.owner = THIS_MODULE; 3188c2ecf20Sopenharmony_ci strlcpy(i2cd->adapter.name, "NVIDIA GPU I2C adapter", 3198c2ecf20Sopenharmony_ci sizeof(i2cd->adapter.name)); 3208c2ecf20Sopenharmony_ci i2cd->adapter.algo = &gpu_i2c_algorithm; 3218c2ecf20Sopenharmony_ci i2cd->adapter.quirks = &gpu_i2c_quirks; 3228c2ecf20Sopenharmony_ci i2cd->adapter.dev.parent = &pdev->dev; 3238c2ecf20Sopenharmony_ci status = i2c_add_adapter(&i2cd->adapter); 3248c2ecf20Sopenharmony_ci if (status < 0) 3258c2ecf20Sopenharmony_ci goto free_irq_vectors; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci status = gpu_populate_client(i2cd, pdev->irq); 3288c2ecf20Sopenharmony_ci if (status < 0) { 3298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "gpu_populate_client failed %d\n", status); 3308c2ecf20Sopenharmony_ci goto del_adapter; 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&pdev->dev, 3000); 3348c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&pdev->dev); 3358c2ecf20Sopenharmony_ci pm_runtime_put_autosuspend(&pdev->dev); 3368c2ecf20Sopenharmony_ci pm_runtime_allow(&pdev->dev); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return 0; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cidel_adapter: 3418c2ecf20Sopenharmony_ci i2c_del_adapter(&i2cd->adapter); 3428c2ecf20Sopenharmony_cifree_irq_vectors: 3438c2ecf20Sopenharmony_ci pci_free_irq_vectors(pdev); 3448c2ecf20Sopenharmony_ci return status; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void gpu_i2c_remove(struct pci_dev *pdev) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci pm_runtime_get_noresume(i2cd->dev); 3528c2ecf20Sopenharmony_ci i2c_del_adapter(&i2cd->adapter); 3538c2ecf20Sopenharmony_ci pci_free_irq_vectors(pdev); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci/* 3578c2ecf20Sopenharmony_ci * We need gpu_i2c_suspend() even if it is stub, for runtime pm to work 3588c2ecf20Sopenharmony_ci * correctly. Without it, lspci shows runtime pm status as "D0" for the card. 3598c2ecf20Sopenharmony_ci * Documentation/power/pci.rst also insists for driver to provide this. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_cistatic __maybe_unused int gpu_i2c_suspend(struct device *dev) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic __maybe_unused int gpu_i2c_resume(struct device *dev) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct gpu_i2c_dev *i2cd = dev_get_drvdata(dev); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci gpu_enable_i2c_bus(i2cd); 3718c2ecf20Sopenharmony_ci /* 3728c2ecf20Sopenharmony_ci * Runtime resume ccgx client so that it can see for any 3738c2ecf20Sopenharmony_ci * connector change event. Old ccg firmware has known 3748c2ecf20Sopenharmony_ci * issue of not triggering interrupt when a device is 3758c2ecf20Sopenharmony_ci * connected to runtime resume the controller. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci pm_request_resume(&i2cd->ccgx_client->dev); 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic UNIVERSAL_DEV_PM_OPS(gpu_i2c_driver_pm, gpu_i2c_suspend, gpu_i2c_resume, 3828c2ecf20Sopenharmony_ci NULL); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic struct pci_driver gpu_i2c_driver = { 3858c2ecf20Sopenharmony_ci .name = "nvidia-gpu", 3868c2ecf20Sopenharmony_ci .id_table = gpu_i2c_ids, 3878c2ecf20Sopenharmony_ci .probe = gpu_i2c_probe, 3888c2ecf20Sopenharmony_ci .remove = gpu_i2c_remove, 3898c2ecf20Sopenharmony_ci .driver = { 3908c2ecf20Sopenharmony_ci .pm = &gpu_i2c_driver_pm, 3918c2ecf20Sopenharmony_ci }, 3928c2ecf20Sopenharmony_ci}; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_cimodule_pci_driver(gpu_i2c_driver); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ajay Gupta <ajayg@nvidia.com>"); 3978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Nvidia GPU I2C controller Driver"); 3988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 399