From: Sanjay R Mehta <sanju.mehta@amd.com>
To: wsa+renesas@sang-engineering.com, jarkko.nikula@linux.intel.com,
andriy.shevchenko@linux.intel.com, jdelvare@suse.de,
Sergey.Semin@baikalelectronics.ru, krzk@kernel.org,
kblaiech@mellanox.com, loic.poulain@linaro.org, rppt@kernel.org,
bjorn.andersson@linaro.org, linux@roeck-us.net,
vadimp@mellanox.com, tali.perry1@gmail.com
Cc: linux-i2c@vger.kernel.org,
Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>,
Sanjay R Mehta <Sanju.Mehta@amd.com>
Subject: [PATCH] i2c: add i2c bus driver for AMD NAVI GPU
Date: Fri, 27 Nov 2020 13:30:39 -0600 [thread overview]
Message-ID: <1606505439-39836-1-git-send-email-sanju.mehta@amd.com> (raw)
From: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>
Latest AMD 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.
Signed-off-by: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>
Co-developed-by: Sanjay R Mehta <Sanju.Mehta@amd.com>
Signed-off-by: Sanjay R Mehta <Sanju.Mehta@amd.com>
---
MAINTAINERS | 7 +
drivers/i2c/busses/Kconfig | 9 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-amdgpu-navi.c | 334 +++++++++++++++++++++++++++++++++++
4 files changed, 351 insertions(+)
create mode 100644 drivers/i2c/busses/i2c-amdgpu-navi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a008b70..59923c7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8187,6 +8187,13 @@ S: Maintained
F: Documentation/i2c/busses/i2c-nvidia-gpu.rst
F: drivers/i2c/busses/i2c-nvidia-gpu.c
+I2C CONTROLLER DRIVER FOR AMD NAVI GPU
+M: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>
+M: Sanjay R Mehta <sanju.mehta@amd.com>
+L: linux-i2c@vger.kernel.org
+S: Maintained
+F: drivers/i2c/busses/i2c-amdgpu-navi.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 a97a9d0..7e268ef 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -322,6 +322,15 @@ config I2C_VIAPRO
This driver can also be built as a module. If so, the module
will be called i2c-viapro.
+config I2C_AMDGPU_NAVI
+ tristate "AMDGPU NAVI I2C controller"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the
+ NAVI 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-amd-gpu.
+
if ACPI
comment "ACPI drivers"
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 683c49f..5818f2d 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o
obj-$(CONFIG_I2C_VIA) += i2c-via.o
obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o
+obj-$(CONFIG_I2C_AMDGPU_NAVI) += i2c-amdgpu-navi.o
# Mac SMBus host controller drivers
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
diff --git a/drivers/i2c/busses/i2c-amdgpu-navi.c b/drivers/i2c/busses/i2c-amdgpu-navi.c
new file mode 100644
index 0000000..3067899
--- /dev/null
+++ b/drivers/i2c/busses/i2c-amdgpu-navi.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+//
+// AMD I2C Controller Driver for Navi GPU's
+//
+// Copyright (c) 2020, Advanced Micro Devices, Inc.
+//
+// Author: Nehal Bakulchandra Shah <Nehal-Bakulchandra.Shah@amd.com>
+// Sanjay R Mehta <Sanju.Mehta@amd.com>
+
+#include <asm/unaligned.h>
+#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 "i2c-designware-core.h"
+
+#define DRIVER_DESC "AMD I2C Controller Driver for Navi"
+#define AMD_UCSI_INTR_REG 0x474
+#define AMD_UCSI_INTR_EN 0xD
+#define AMD_MASTERCFG_MASK GENMASK_ULL(15, 0)
+
+struct amdgpu_i2c_dev {
+ void __iomem *regs;
+ struct device *dev;
+ u32 master_cfg;
+ u32 slave_adr;
+ u32 tx_fifo_depth;
+ u32 rx_fifo_depth;
+ u32 sda_hold_time;
+ u16 ss_hcnt;
+ u16 ss_lcnt;
+ u16 fs_hcnt;
+ u16 fs_lcnt;
+ u16 fp_hcnt;
+ u16 fp_lcnt;
+ u16 hs_hcnt;
+ u16 hs_lcnt;
+ struct i2c_adapter adapter;
+ struct i2c_board_info *gpu_ccgx_ucsi;
+ struct i2c_client *ccgx_client;
+};
+
+static void amdgpu_configure_i2c_bus(struct amdgpu_i2c_dev *i2cd)
+{
+ u16 icon;
+
+ /* First disable the controller */
+ writel(0, i2cd->regs + DW_IC_ENABLE);
+ i2cd->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | DW_IC_CON_RESTART_EN
+ | DW_IC_CON_SPEED_STD;
+
+ /* clear all the interrupt */
+ readl(i2cd->regs + DW_IC_CLR_INTR);
+ writel(0, i2cd->regs + DW_IC_INTR_MASK);
+
+ icon = i2cd->master_cfg & AMD_MASTERCFG_MASK;
+ icon &= ~BIT(3);
+ icon &= ~DW_IC_CON_10BITADDR_MASTER;
+ icon = icon | DW_IC_CON_SPEED_STD;
+ /* configure the master */
+ writel(icon, i2cd->regs + DW_IC_CON);
+ /*configure the FIFO */
+ i2cd->tx_fifo_depth = 32;
+ i2cd->rx_fifo_depth = 32;
+ writel(i2cd->tx_fifo_depth, i2cd->regs + DW_IC_TX_TL);
+ writel(i2cd->rx_fifo_depth, i2cd->regs + DW_IC_RX_TL);
+
+ /* setup 100k Speed */
+ i2cd->ss_hcnt = 430;
+ i2cd->ss_lcnt = 570;
+ writel(i2cd->ss_hcnt, i2cd->regs + DW_IC_SS_SCL_HCNT);
+ writel(i2cd->ss_lcnt, i2cd->regs + DW_IC_SS_SCL_LCNT);
+ /* setup the slave address */
+ i2cd->slave_adr = 0x08;
+ writel(i2cd->slave_adr, i2cd->regs + DW_IC_TAR);
+
+ /* Now Enable the*/
+ writel(1, i2cd->regs + DW_IC_ENABLE);
+}
+
+static int amdgpu_i2c_check_activity(struct amdgpu_i2c_dev *i2cd)
+{
+ int timeout = ETIMEDOUT;
+
+ while (readl(i2cd->regs + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
+ if (timeout <= 0) {
+ dev_dbg(i2cd->dev, "timeout waiting for bus ready\n");
+ if (readl(i2cd->regs + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY)
+ return -ETIMEDOUT;
+ return 0;
+ }
+ timeout--;
+ usleep_range(1000, 1100);
+ }
+
+ return 0;
+}
+
+static int amdgpu_i2c_check_stopbit(struct amdgpu_i2c_dev *i2cd)
+{
+ int timeout = ETIMEDOUT;
+
+ while (readl(i2cd->regs + DW_IC_INTR_STAT) & DW_IC_INTR_STOP_DET) {
+ if (timeout <= 0) {
+ dev_dbg(i2cd->dev, "timeout waiting for STOP BIT ready\n");
+ if (readl(i2cd->regs + DW_IC_INTR_STAT) & DW_IC_INTR_STOP_DET)
+ return -ETIMEDOUT;
+ return 0;
+ }
+ timeout--;
+ usleep_range(1000, 1100);
+ }
+
+ return 0;
+}
+
+static int amdgpu_i2c_status(struct amdgpu_i2c_dev *i2cd)
+{
+ int status;
+
+ status = amdgpu_i2c_check_activity(i2cd);
+ if (status)
+ return -ETIMEDOUT;
+
+ status = amdgpu_i2c_check_stopbit(i2cd);
+ if (status)
+ return -ETIMEDOUT;
+
+ return status;
+}
+
+/* Polling based xfer routine */
+static int amdgpu_i2c_master_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct amdgpu_i2c_dev *i2cd = i2c_get_adapdata(adap);
+ int i, j, len, k;
+ int cmd = 0;
+ int status;
+ u8 *buf;
+
+ pm_runtime_get_sync(i2cd->dev);
+ amdgpu_configure_i2c_bus(i2cd);
+
+ for (i = 0; i < num; i++) {
+ buf = msgs[i].buf;
+ len = msgs[i].len;
+
+ if (!msgs[i].flags & I2C_M_RD)
+ writel(len - 1, i2cd->regs + DW_IC_TX_TL);
+
+ for (j = len; j > 0; j--) {
+ if (i == num - 1 && j == 1)
+ cmd |= BIT(9);
+
+ if (msgs[i].flags & I2C_M_RD) {
+ writel(0x100, i2cd->regs + DW_IC_DATA_CMD);
+ writel(0x100 | cmd, i2cd->regs + DW_IC_DATA_CMD);
+ if (cmd) {
+ writel(2 * (len - 1), i2cd->regs + DW_IC_TX_TL);
+ writel(2 * (len - 1), i2cd->regs + DW_IC_RX_TL);
+ status = amdgpu_i2c_status(i2cd);
+ if (status)
+ return -ETIMEDOUT;
+
+ for (k = 0; k < len; k++)
+ buf[k] = readl(i2cd->regs + DW_IC_DATA_CMD);
+ status = amdgpu_i2c_check_stopbit(i2cd);
+ if (status)
+ return -ETIMEDOUT;
+ }
+ } else {
+ writel(*buf++ | cmd, i2cd->regs + DW_IC_DATA_CMD);
+ usleep_range(10000, 11000);
+ }
+ }
+ status = amdgpu_i2c_check_stopbit(i2cd);
+ if (status)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static u32 amdgpu_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm amdgpu_i2c_algorithm = {
+ .master_xfer = amdgpu_i2c_master_xfer,
+ .functionality = amdgpu_i2c_functionality,
+};
+
+#define PCI_DEVICE_ID_UCSI_AMD 0x73a4
+static const struct pci_device_id amdgpu_i2c_ids[] = {
+ {PCI_VDEVICE(ATI, PCI_DEVICE_ID_UCSI_AMD)},
+ {}
+};
+MODULE_DEVICE_TABLE(pci, amdgpu_i2c_ids);
+
+static int amdgpu_populate_client(struct amdgpu_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->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi);
+ if (!i2cd->ccgx_client)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int amdgpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct amdgpu_i2c_dev *i2cd;
+ int status;
+ int irq;
+
+ i2cd = devm_kzalloc(&pdev->dev, sizeof(*i2cd), GFP_KERNEL);
+ if (!i2cd)
+ return -ENOMEM;
+
+ i2cd->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, i2cd);
+ status = pcim_enable_device(pdev);
+ if (status < 0) {
+ dev_err(&pdev->dev, "pcim_enable_device failed %d\n", status);
+ return status;
+ }
+
+ pci_set_master(pdev);
+
+ i2cd->regs = pcim_iomap(pdev, 0, 0);
+ if (!i2cd->regs) {
+ dev_err(&pdev->dev, "pcim_iomap failed\n");
+ return -ENOMEM;
+ }
+
+ /* Enable ucsi interrupt */
+ writel(AMD_UCSI_INTR_EN, i2cd->regs + AMD_UCSI_INTR_REG);
+
+ status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
+ if (status < 0) {
+ dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", status);
+ return status;
+ }
+
+ irq = pci_irq_vector(pdev, 0);
+ amdgpu_configure_i2c_bus(i2cd);
+ i2c_set_adapdata(&i2cd->adapter, i2cd);
+ i2cd->adapter.owner = THIS_MODULE;
+ strlcpy(i2cd->adapter.name, "AMDGPU NAVI I2C adapter",
+ sizeof(i2cd->adapter.name));
+ i2cd->adapter.algo = &amdgpu_i2c_algorithm;
+ i2cd->adapter.dev.parent = &pdev->dev;
+ status = i2c_add_adapter(&i2cd->adapter);
+ if (status < 0)
+ goto free_irq_vectors;
+
+ status = amdgpu_populate_client(i2cd, irq);
+ if (status < 0) {
+ dev_err(&pdev->dev, "amdgpu_populate_client failed %d\n", status);
+ goto del_adapter;
+ }
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 3000);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
+
+ return 0;
+
+del_adapter:
+ i2c_del_adapter(&i2cd->adapter);
+free_irq_vectors:
+ pci_free_irq_vectors(pdev);
+ return status;
+}
+
+static void amdgpu_i2c_remove(struct pci_dev *pdev)
+{
+ struct amdgpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev);
+
+ writel(0, i2cd->regs + AMD_UCSI_INTR_REG);
+ pm_runtime_get_noresume(i2cd->dev);
+ i2c_del_adapter(&i2cd->adapter);
+ pci_free_irq_vectors(pdev);
+}
+
+static __maybe_unused int amdgpu_i2c_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static __maybe_unused int amdgpu_i2c_resume(struct device *dev)
+{
+ struct amdgpu_i2c_dev *i2cd = dev_get_drvdata(dev);
+
+ amdgpu_configure_i2c_bus(i2cd);
+ pm_request_resume(&i2cd->ccgx_client->dev);
+ return 0;
+}
+
+static UNIVERSAL_DEV_PM_OPS(amdgpu_i2c_driver_pm, amdgpu_i2c_suspend, amdgpu_i2c_resume,
+ NULL);
+
+static struct pci_driver amdgpu_i2c_driver = {
+ .name = "amdgpu-ucsi-i2c",
+ .id_table = amdgpu_i2c_ids,
+ .probe = amdgpu_i2c_probe,
+ .remove = amdgpu_i2c_remove,
+ .driver = {
+ .pm = &amdgpu_i2c_driver_pm,
+ },
+};
+module_pci_driver(amdgpu_i2c_driver);
+
+MODULE_AUTHOR("Nehal Bakulchandra Shah <Nehal-bakulchandra.Shah@amd.com>");
+MODULE_AUTHOR("Sanjay R Mehta <Sanju.Mehta@amd.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("Dual BSD/GPL");
--
2.7.4
next reply other threads:[~2020-11-28 22:09 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-11-27 19:30 Sanjay R Mehta [this message]
2020-11-30 11:19 ` [PATCH] i2c: add i2c bus driver for AMD NAVI GPU Andy Shevchenko
2020-12-02 4:44 ` Sanjay R Mehta
-- strict thread matches above, loose matches on Subject: below --
2021-03-09 13:31 [PATCH] i2c: add i2c bus driver for amd navi gpu Sanket Goswami
2021-03-09 14:23 ` kernel test robot
2021-03-09 14:26 ` Andy Shevchenko
2021-03-22 16:56 ` Goswami, Sanket
2021-03-25 17:05 ` Andy Shevchenko
2021-03-26 10:23 ` Goswami, Sanket
2021-03-26 10:40 ` Andy Shevchenko
2021-03-29 5:55 ` Goswami, Sanket
2021-03-29 12:36 ` Andy Shevchenko
2021-03-09 15:35 ` kernel test robot
2021-03-09 15:36 ` kernel test robot
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=1606505439-39836-1-git-send-email-sanju.mehta@amd.com \
--to=sanju.mehta@amd.com \
--cc=Nehal-Bakulchandra.Shah@amd.com \
--cc=Sergey.Semin@baikalelectronics.ru \
--cc=andriy.shevchenko@linux.intel.com \
--cc=bjorn.andersson@linaro.org \
--cc=jarkko.nikula@linux.intel.com \
--cc=jdelvare@suse.de \
--cc=kblaiech@mellanox.com \
--cc=krzk@kernel.org \
--cc=linux-i2c@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=loic.poulain@linaro.org \
--cc=rppt@kernel.org \
--cc=tali.perry1@gmail.com \
--cc=vadimp@mellanox.com \
--cc=wsa+renesas@sang-engineering.com \
/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).