Hi Andy, > > Latest NVIDIA GPU card has USB Type-C interface. There is a Type-C > > controller which can be accessed over I2C. > > > > This driver adds I2C bus driver to communicate with Type-C controller. > > I2C client driver will be part of USB Type-C UCSI driver. > > Thanks for an update, my comments below. Thanks for reviewing. > > +#include <linux/delay.h> > > +#include <linux/i2c.h> > > +#include <linux/interrupt.h> > > +#include <linux/module.h> > > +#include <linux/pci.h> > > +#include <linux/platform_device.h> > > +#include <linux/pm.h> > > +#include <linux/pm_runtime.h> > > + blank line. Ok. I could not find kernel documentation which requires this blank line. Can you please point me to it? > > +#include <asm/unaligned.h> > > > +struct gpu_i2c_dev { > > + struct device *dev; > > + void __iomem *regs; > > + struct i2c_adapter adapter; > > + struct i2c_client *client; > > + struct mutex mutex; /* to sync read/write */ > > + bool do_start; > > +}; > > > +static int i2c_check_status(struct gpu_i2c_dev *i2cd) { > > + unsigned long target = jiffies + msecs_to_jiffies(1000); > > + u32 val; > > + > > + do { > > + val = readl(i2cd->regs + I2C_MST_CNTL); > > > + if ((val & I2C_MST_CNTL_CYCLE_TRIGGER) != > > + I2C_MST_CNTL_CYCLE_TRIGGER) > > Part after != redundant since it's one bit. > But I'm fine with current as well. Ok, will fix. > > > + break; > > + if ((val & I2C_MST_CNTL_STATUS) != > > + I2C_MST_CNTL_STATUS_BUS_BUSY) > > + break; > > + usleep_range(1000, 2000); > > + } while (time_is_after_jiffies(target)); > > > + > > Redundant. Ok, I will remove it but I didn't understand why its redundant? I thought adding extra line would be more readable. > > + if (time_is_before_jiffies(target)) > > + return -EIO; > > + > > + val = readl(i2cd->regs + I2C_MST_CNTL); > > + switch (val & I2C_MST_CNTL_STATUS) { > > + case I2C_MST_CNTL_STATUS_OKAY: > > + return 0; > > + case I2C_MST_CNTL_STATUS_NO_ACK: > > + return -EIO; > > + case I2C_MST_CNTL_STATUS_TIMEOUT: > > + return -ETIME; > > + case I2C_MST_CNTL_STATUS_BUS_BUSY: > > + return -EBUSY; > > + default: > > > + break; > > return 0; ? Ok, will fix. > > > + } > > > + return 0; > > See above. Ok > > > +} > > > +static int i2c_write(struct gpu_i2c_dev *i2cd, u8 data) { > > + u32 val; > > + > > + writel(data, i2cd->regs + I2C_MST_DATA); > > + > > + val = I2C_MST_CNTL_CMD_WRITE | (1 << > I2C_MST_CNTL_BURST_SIZE_SHIFT) | > > + I2C_MST_CNTL_GEN_NACK; > > > + val &= ~(I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_GEN_STOP > > + | I2C_MST_CNTL_GEN_RAB); > > "|" should be on previous line to follow common style in this module. The "|" here is to clear the bit together. [val &= ~(X | Y | Z)] Style in this module is still followed. Please see first line which does "|" to val. > > + writel(val, i2cd->regs + I2C_MST_CNTL); > > + > > + return i2c_check_status(i2cd); } > > + > > +static int gpu_i2c_master_xfer(struct i2c_adapter *adap, > > + struct i2c_msg *msgs, int num) { > > + struct gpu_i2c_dev *i2cd = i2c_get_adapdata(adap); > > + struct device *dev = i2cd->dev; > > + int status; > > + int i, j; > > > +stop: > > + status = i2c_stop(i2cd); > > + if (status < 0) > > + dev_err(dev, "i2c_stop error %x", status); > > +unlock: > > + mutex_unlock(&i2cd->mutex); > > > + return i; > > Shouldn't it return status in case of error? Thanks, will fix. > > +} > > > +/* > > + * This driver is for the Nvidia GPU cards with USB Type-C interface. > > + * We want to identify the cards using vendor ID and class code. > > + * Currently there is no class code defined for UCSI device over PCI > > + * so using UNKNOWN. > > + */ > > So, I didn't see how it *guarantees* no collision with other devices of the > same class... I checked and there is no other NVIDIA cards with UNKNOWN class code. Moreover, even if the driver gets loaded for a wrong card then eventually i2c_read() (initiated from UCSI driver) will timeout or UCSI commands will timeout. I think this is safe enough for now. We will update the class code when UCSI gets a PCI class code. We don't want to add dependency of adding product id for any new card which requires this driver. > > +#define PCI_CLASS_SERIAL_UNKNOWN 0x0c80 > > +static const struct pci_device_id gpu_i2c_ids[] = { > > > + { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, > > + PCI_CLASS_SERIAL_UNKNOWN << 8, 0xffffff00}, > > ...for now, I would suggest to be more stricted, i.e. > > { PCI_VDEVICE(NVIDIA, 0x1ad9) }, > > Whenever the class appears it can be added later on. See above. > > > + { } > > +}; > > +MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); > > + > > > +static int gpu_i2c_probe(struct pci_dev *dev, const struct > > +pci_device_id *id) > > Use pdev instead of dev to distinguish struct device from struct pci_dev type > in variable name. ok > > > +static void gpu_i2c_remove(struct pci_dev *dev) > > Ditto. > > > + struct gpu_i2c_dev *i2cd = pci_get_drvdata(to_pci_dev(dev)); > Isn't the same as dev_get_drvdata() ? ok > > > + struct gpu_i2c_dev *i2cd = pci_get_drvdata(to_pci_dev(dev)); > > Ditto. Ok Thanks Ajay -- nvpublic -- > > -- > With Best Regards, > Andy Shevchenko