From: Ajay Gupta <ajayg@nvidia.com>
To: Peter Rosin <peda@axentia.se>,
"wsa@the-dreams.de" <wsa@the-dreams.de>,
"heikki.krogerus@linux.intel.com"
<heikki.krogerus@linux.intel.com>
Cc: "linux-usb@vger.kernel.org" <linux-usb@vger.kernel.org>,
"linux-i2c@vger.kernel.org" <linux-i2c@vger.kernel.org>
Subject: [v12,1/2] i2c: buses: add i2c bus driver for NVIDIA GPU
Date: Wed, 12 Sep 2018 22:22:09 +0000 [thread overview]
Message-ID: <4d00f1db072a4fc2a7a49e09b503fe72@bgmail101.nvidia.com> (raw)
Hi Peter,
> >>> This driver adds I2C bus driver to communicate with Type-C controller.
> >>> I2C client driver will be part of USB Type-C UCSI driver.
> >>>
> >>> Signed-off-by: Ajay Gupta <ajayg@nvidia.com>
> >>> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
> >>> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> >>> ---
> >>> Changes from v1 -> v2
> >>> None
> >>> Changes from v2 -> v3
> >>> Fixed review comments from Andy and Thierry
> >>> Rename i2c-gpu.c -> i2c-nvidia-gpu.c Changes from v3 -> v4
> >>> Fixed review comments from Andy
> >>> Changes from v4 -> v5
> >>> Fixed review comments from Andy
> >>> Changes from v5 -> v6
> >>> None
> >>> Changes from v6 -> v7 -> v8
> >>> Fixed review comments from Peter
> >>> - Add implicit STOP for last write message
> >>> - Add i2c_adapter_quirks with max_read_len and
> >>> I2C_AQ_COMB flags
> >>> Changes from v8 -> v9
> >>> Fixed review comments from Peter
> >>> - Drop do_start flag
> >>> - Use i2c_8bit_addr_from_msg()
> >>> Changes from v9 -> v10
> >>> Fixed review comments from Peter
> >>> - Dropped I2C_FUNC_SMBUS_EMUL
> >>> - Dropped local mutex
> >>> Changes from v10 -> v11
> >>> Fixed review comments from Peter
> >>> - Moved stop in master_xfer at end of message
> >>> - Change i2c_read without STOP
> >>> - Dropped I2C_AC_COMB* flags
> >>> Changes from v11 -> v12
> >>> Fixed review comments from Peter
> >>> - Removed clearing of empty bits
> >>> - Fix master_xfer for correct stop use
> >>>
> >>> Documentation/i2c/busses/i2c-nvidia-gpu | 18 ++
> >>> MAINTAINERS | 7 +
> >>> drivers/i2c/busses/Kconfig | 9 +
> >>> drivers/i2c/busses/Makefile | 1 +
> >>> drivers/i2c/busses/i2c-nvidia-gpu.c | 368
> >> ++++++++++++++++++++++++++++++++
> >>> 5 files changed, 403 insertions(+)
> >>> create mode 100644 Documentation/i2c/busses/i2c-nvidia-gpu
> >>> create mode 100644 drivers/i2c/busses/i2c-nvidia-gpu.c
> >>>
> >>> diff --git a/Documentation/i2c/busses/i2c-nvidia-gpu
> >>> b/Documentation/i2c/busses/i2c-nvidia-gpu
> >>> new file mode 100644
> >>> index 0000000..31884d2
> >>> --- /dev/null
> >>> +++ b/Documentation/i2c/busses/i2c-nvidia-gpu
> >>> @@ -0,0 +1,18 @@
> >>> +Kernel driver i2c-nvidia-gpu
> >>> +
> >>> +Datasheet: not publicly available.
> >>> +
> >>> +Authors:
> >>> + Ajay Gupta <ajayg@nvidia.com>
> >>> +
> >>> +Description
> >>> +-----------
> >>> +
> >>> +i2c-nvidia-gpu is a driver for I2C controller included in NVIDIA
> >>> +Turing and later GPUs and it is used to communicate with Type-C
> >>> +controller
> >> on GPUs.
> >>> +
> >>> +If your 'lspci -v' listing shows something like the following,
> >>> +
> >>> +01:00.3 Serial bus controller [0c80]: NVIDIA Corporation Device
> >>> +1ad9 (rev a1)
> >>> +
> >>> +then this driver should support the I2C controller of your GPU.
> >>> diff --git a/MAINTAINERS b/MAINTAINERS index d870cb5..b71b0b4
> 100644
> >>> --- a/MAINTAINERS
> >>> +++ b/MAINTAINERS
> >>> @@ -6798,6 +6798,13 @@ L: linux-acpi@vger.kernel.org
> >>> S: Maintained
> >>> F: drivers/i2c/i2c-core-acpi.c
> >>>
> >>> +I2C CONTROLLER DRIVER FOR NVIDIA GPU
> >>> +M: Ajay Gupta <ajayg@nvidia.com>
> >>> +L: linux-i2c@vger.kernel.org
> >>> +S: Maintained
> >>> +F: Documentation/i2c/busses/i2c-nvidia-gpu
> >>> +F: drivers/i2c/busses/i2c-nvidia-gpu.c
> >>> +
> >>> I2C MUXES
> >>> M: Peter Rosin <peda@axentia.se>
> >>> L: linux-i2c@vger.kernel.org
> >>> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> >>> index 451d4ae..eed827b 100644
> >>> --- a/drivers/i2c/busses/Kconfig
> >>> +++ b/drivers/i2c/busses/Kconfig
> >>> @@ -224,6 +224,15 @@ config I2C_NFORCE2_S4985
> >>> This driver can also be built as a module. If so, the module
> >>> will be called i2c-nforce2-s4985.
> >>>
> >>> +config I2C_NVIDIA_GPU
> >>> + tristate "NVIDIA GPU I2C controller"
> >>> + depends on PCI
> >>> + help
> >>> + 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
> >>> + Type-C controller. This driver can also be built as a module called
> >>> + i2c-nvidia-gpu.
> >>> +
> >>> config I2C_SIS5595
> >>> tristate "SiS 5595"
> >>> depends on PCI
> >>> diff --git a/drivers/i2c/busses/Makefile
> >>> b/drivers/i2c/busses/Makefile index 18b26af..d499813 100644
> >>> --- a/drivers/i2c/busses/Makefile
> >>> +++ b/drivers/i2c/busses/Makefile
> >>> @@ -140,5 +140,6 @@ obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
> >>> obj-$(CONFIG_I2C_XGENE_SLIMPRO) += i2c-xgene-slimpro.o
> >>> obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
> >>> obj-$(CONFIG_I2C_FSI) += i2c-fsi.o
> >>> +obj-$(CONFIG_I2C_NVIDIA_GPU) += i2c-nvidia-gpu.o
> >>>
> >>> ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG diff --git
> >>> a/drivers/i2c/busses/i2c-nvidia-gpu.c
> >>> b/drivers/i2c/busses/i2c-nvidia-gpu.c
> >>> new file mode 100644
> >>> index 0000000..0c16b0a
> >>> --- /dev/null
> >>> +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c
> >>> @@ -0,0 +1,368 @@
> >>> +// SPDX-License-Identifier: GPL-2.0
> >>> +/*
> >>> + * Nvidia GPU I2C controller Driver
> >>> + *
> >>> + * Copyright (C) 2018 NVIDIA Corporation. All rights reserved.
> >>> + * Author: Ajay Gupta <ajayg@nvidia.com> */ #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>
> >>> +
> >>> +#include <asm/unaligned.h>
> >>> +
> >>> +/* I2C definitions */
> >>> +#define I2C_MST_CNTL 0x00
> >>> +#define I2C_MST_CNTL_GEN_START BIT(0)
> >>> +#define I2C_MST_CNTL_GEN_STOP BIT(1)
> >>> +#define I2C_MST_CNTL_CMD_READ (1 << 2)
> >>> +#define I2C_MST_CNTL_CMD_WRITE (2 << 2)
> >>> +#define I2C_MST_CNTL_BURST_SIZE_SHIFT 6
> >>> +#define I2C_MST_CNTL_GEN_NACK BIT(28)
> >>> +#define I2C_MST_CNTL_STATUS GENMASK(30, 29)
> >>> +#define I2C_MST_CNTL_STATUS_OKAY (0 << 29)
> >>> +#define I2C_MST_CNTL_STATUS_NO_ACK (1 << 29)
> >>> +#define I2C_MST_CNTL_STATUS_TIMEOUT (2 << 29)
> >>> +#define I2C_MST_CNTL_STATUS_BUS_BUSY (3 << 29)
> >>> +#define I2C_MST_CNTL_CYCLE_TRIGGER BIT(31)
> >>> +
> >>> +#define I2C_MST_ADDR 0x04
> >>> +
> >>> +#define I2C_MST_I2C0_TIMING 0x08
> >>> +#define I2C_MST_I2C0_TIMING_SCL_PERIOD_100KHZ 0x10e
> >>> +#define I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT 16
> >>> +#define I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT_MAX 255
> >>> +#define I2C_MST_I2C0_TIMING_TIMEOUT_CHECK BIT(24)
> >>> +
> >>> +#define I2C_MST_DATA 0x0c
> >>> +
> >>> +#define I2C_MST_HYBRID_PADCTL 0x20
> >>> +#define I2C_MST_HYBRID_PADCTL_MODE_I2C BIT(0)
> >>> +#define I2C_MST_HYBRID_PADCTL_I2C_SCL_INPUT_RCV
> >> BIT(14)
> >>> +#define I2C_MST_HYBRID_PADCTL_I2C_SDA_INPUT_RCV
> >> BIT(15)
> >>> +
> >>> +struct gpu_i2c_dev {
> >>> + struct device *dev;
> >>> + void __iomem *regs;
> >>> + struct i2c_adapter adapter;
> >>> + struct i2c_board_info *gpu_ccgx_ucsi; };
> >>> +
> >>> +static void gpu_enable_i2c_bus(struct gpu_i2c_dev *i2cd) {
> >>> + u32 val;
> >>> +
> >>> + /* enable I2C */
> >>> + val = readl(i2cd->regs + I2C_MST_HYBRID_PADCTL);
> >>> + val |= I2C_MST_HYBRID_PADCTL_MODE_I2C |
> >>> + I2C_MST_HYBRID_PADCTL_I2C_SCL_INPUT_RCV |
> >>> + I2C_MST_HYBRID_PADCTL_I2C_SDA_INPUT_RCV;
> >>> + writel(val, i2cd->regs + I2C_MST_HYBRID_PADCTL);
> >>> +
> >>> + /* enable 100KHZ mode */
> >>> + val = I2C_MST_I2C0_TIMING_SCL_PERIOD_100KHZ;
> >>> + val |= (I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT_MAX
> >>> + << I2C_MST_I2C0_TIMING_TIMEOUT_CLK_CNT);
> >>> + val |= I2C_MST_I2C0_TIMING_TIMEOUT_CHECK;
> >>> + writel(val, i2cd->regs + I2C_MST_I2C0_TIMING); }
> >>> +
> >>> +static int gpu_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))
> >>
> >> It occurred to me that generating NACK only makes sense for reads,
> >> and that you only want to have a NACK after the final byte in a read
> >> messages. So, here's a new attempt.
> >>
> >> What if you replace the above test with
> >>
> >> if (!(val & I2C_MST_CNTL_READ))
> >>
> >> (since all cycle-triggers are also reads, at least currently, and we
> >> also want to wait for the tail reads to complete before we try to get
> >> the received data from the register)
> >>
> >> and then...
> >>
> >>> + break;
> >>> + if ((val & I2C_MST_CNTL_STATUS) !=
> >>> + I2C_MST_CNTL_STATUS_BUS_BUSY)
> >>> + break;
> >>> + usleep_range(1000, 2000);
> >>> + } while (time_is_after_jiffies(target));
> >>> +
> >>> + if (time_is_before_jiffies(target)) {
> >>> + dev_err(i2cd->dev, "i2c timeout error %x\n", val);
> >>> + return -ETIME;
> >>> + }
> >>> +
> >>> + 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:
> >>> + return 0;
> >>> + }
> >>> +}
> >>> +
> >>> +static int gpu_i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16
> >>> +len) {
> >>> + int status;
> >>> + u32 val;
> >>> +
> >>> + val = I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_CMD_READ |
> >>> + (len << I2C_MST_CNTL_BURST_SIZE_SHIFT) |
> >>> + I2C_MST_CNTL_CYCLE_TRIGGER |
> >> I2C_MST_CNTL_GEN_NACK;
> >>> + writel(val, i2cd->regs + I2C_MST_CNTL);
> >>> +
> >>> + status = gpu_i2c_check_status(i2cd);
> >>> + if (status < 0)
> >>> + return status;
> >>> +
> >>> + val = readl(i2cd->regs + I2C_MST_DATA);
> >>> + switch (len) {
> >>> + case 1:
> >>> + data[0] = val;
> >>> + break;
> >>> + case 2:
> >>> + put_unaligned_be16(val, data);
> >>> + break;
> >>> + case 3:
> >>> + put_unaligned_be16(val >> 8, data);
> >>> + data[2] = val;
> >>> + break;
> >>> + case 4:
> >>> + put_unaligned_be32(val, data);
> >>> + break;
> >>> + default:
> >>> + break;
> >>> + }
> >>> + return status;
> >>> +}
> >>
> >> ...replace your gpu_i2c_read with this:
> >>
> >> #define GPU_MAX_BURST 1
> >> static int gpu_i2c_read(struct gpu_i2c_dev *i2cd, u8 *data, u16 len) {
> >> u16 burst = min(len, GPU_MAX_BURST);
> >> int status;
> >> u32 val;
> >>
> >> val = I2C_MST_CNTL_GEN_START | I2C_MST_CNTL_CMD_READ |
> >> (burst << I2C_MST_CNTL_BURST_SIZE_SHIFT) |
> >> I2C_MST_CNTL_CYCLE_TRIGGER;
> >>
> >> for (;;) {
> >> if (len <= GPU_MAX_BURST)
> >> val |= I2C_MST_CNTL_GEN_NACK;
> >> writel(val, i2cd->regs + I2C_MST_CNTL);
> >>
> >> status = gpu_i2c_check_status(i2cd);
> >> if (status < 0)
> >> return status;
> >>
> >> val = readl(i2cd->regs + I2C_MST_DATA);
> >> switch (burst) {
> >> case 1:
> >> data[0] = val;
> >> break;
> >> case 2:
> >> put_unaligned_be16(val, data);
> >> break;
> >> case 3:
> >> put_unaligned_be16(val >> 8, data);
> >> data[2] = val;
> >> break;
> >> case 4:
> >> put_unaligned_be32(val, data);
> >> break;
> >> default:
> >> break;
> >> }
> >>
> >> if (len <= GPU_MAX_BURST)
> >> break;
> >>
> >> data += GPU_MAX_BURST;
> >> len -= GPU_MAX_BURST;
> >>
> >> burst = min(len, GPU_MAX_BURST);
> >> val = I2C_MST_CNTL_CMD_READ |
> >> (burst << I2C_MST_CNTL_BURST_SIZE_SHIFT);
> >> }
> >>
> >> return status;
> >> }
> >>
> >> If that works,
> >> then change GPU_MAX_BURST to 4, drop the quirk and the splitting of
> >> the reads in patch 2/2 and check that too...
> >>
> >> *fingers crossed*
> > Unfortunately, that also didn't work. I tried some tweaks with
> > _TRIGGER and _START but that also didn't help.
>
> Did you notice the above change to gpu_i2c_check_status?
Yes.
> I assume so and
> that there simply is something about the chip that is not understood.
Can we get the working set in while the issues is being debugged?
> >>> +static int gpu_populate_client(struct gpu_i2c_dev *i2cd, int irq) {
> >>> + struct i2c_client *ccgx_client;
> >>> +
> >>> + i2cd->gpu_ccgx_ucsi = devm_kzalloc(i2cd->dev,
> >>> + sizeof(struct i2c_board_info),
> >>
> >> I prefer sizeof(*i2cd->gpu_ccgx_ucsi). Especially when the type is
> >> far away as it is here...
> > First one looks more readable and cleaner to me but will change.
> >
> > sizeof(struct i2c_board_info),
> > sizeof(*i2cd->gpu_ccgx_ucsi),
>
> In my experience, the latter variant is easier to keep correct if/when the code
> is changed. But yes, it is a matter of taste...
>
> >>> + 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;
> >>> + ccgx_client = i2c_new_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
> >>> + if (!ccgx_client)
> >>> + return -ENODEV;
> >>> +
> >>> + return 0;
> >>> +}
> >>> +
> >>> +static int gpu_i2c_probe(struct pci_dev *pdev, const struct
> >>> +pci_device_id *id) {
> >>> + struct gpu_i2c_dev *i2cd;
> >>> + int status;
> >>> +
> >>> + i2cd = devm_kzalloc(&pdev->dev, sizeof(struct gpu_i2c_dev),
> >>> +GFP_KERNEL);
> >>
> >> While at it, you might as well change this to sizeof(*i2cd), and
> > Ok.
> >
> >> please check for the pattern in patch 2/2.
> > I hope you saw the latest response at [1]. The change works on
> > multiple systems and no error has been reported yet.
> > What else (and how) to check ?
> >
> > [1] https://marc.info/?l=linux-usb&m=153667127502959&w=2
>
> Yes, I saw that. I generally assume that patches work for the sender, but if I
> see something that I can't understand how it can work, it tend to not hold on
> to the assumption and explicitly ask if the code has in fact been tested...
So I assume nothing more left to check on this for now.
Thanks
Ajay
--nvpublic
> Cheers,
> Peter
next reply other threads:[~2018-09-12 22:22 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-09-12 22:22 Ajay Gupta [this message]
-- strict thread matches above, loose matches on Subject: below --
2018-10-24 19:44 [v12,1/2] i2c: buses: add i2c bus driver for NVIDIA GPU Greg Kroah-Hartman
2018-10-24 17:46 Ajay Gupta
2018-10-16 23:21 Wolfram Sang
2018-10-16 17:46 Peter Rosin
2018-10-02 16:34 Ajay Gupta
2018-10-02 7:27 Wolfram Sang
2018-10-01 22:04 Ajay Gupta
2018-10-01 21:16 Ajay Gupta
2018-10-01 19:51 Peter Rosin
2018-10-01 19:37 Wolfram Sang
2018-10-01 19:33 Ajay Gupta
2018-09-13 23:32 Ajay Gupta
2018-09-13 11:11 Heikki Krogerus
2018-09-13 7:36 Peter Rosin
2018-09-12 21:57 Peter Rosin
2018-09-12 18:02 Ajay Gupta
2018-09-12 11:02 Peter Rosin
2018-09-12 10:25 Peter Rosin
2018-09-11 22:48 Ajay Gupta
2018-09-11 20:57 Peter Rosin
2018-09-11 17:45 Ajay Gupta
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4d00f1db072a4fc2a7a49e09b503fe72@bgmail101.nvidia.com \
--to=ajayg@nvidia.com \
--cc=heikki.krogerus@linux.intel.com \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=peda@axentia.se \
--cc=wsa@the-dreams.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).