summaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authorArminder Singh <arminders208@outlook.com>2022-11-05 07:56:49 -0400
committerWolfram Sang <wsa@kernel.org>2022-11-12 21:30:16 +0100
commite826192cc26bd69746bbf22e6bdf72b87cb3d97b (patch)
tree4804e25a3a288f03b4eddb69a05f72e32ee1d574 /drivers/i2c
parent8502bee5584235943c4d371597c740d6779991db (diff)
downloadlinux-stable-e826192cc26bd69746bbf22e6bdf72b87cb3d97b.tar.gz
linux-stable-e826192cc26bd69746bbf22e6bdf72b87cb3d97b.tar.bz2
linux-stable-e826192cc26bd69746bbf22e6bdf72b87cb3d97b.zip
i2c: /pasemi: PASemi I2C controller IRQ enablement
This patch adds IRQ support to the PASemi I2C controller driver to increase the performace of I2C transactions on platforms with PASemi I2C controllers. While primarily intended for Apple silicon platforms, this patch should also help in enabling IRQ support for older PASemi hardware as well should the need arise. This version of the patch has been tested on an M1 Ultra Mac Studio, as well as an M1 MacBook Pro, and userspace launches successfully while using the IRQ path for I2C transactions. Signed-off-by: Arminder Singh <arminders208@outlook.com> Reviewed-by: Sven Peter <sven@svenpeter.dev> Reviewed-by: Hector Martin <marcan@marcan.st> Signed-off-by: Wolfram Sang <wsa@kernel.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-pasemi-core.c32
-rw-r--r--drivers/i2c/busses/i2c-pasemi-core.h5
-rw-r--r--drivers/i2c/busses/i2c-pasemi-platform.c6
3 files changed, 38 insertions, 5 deletions
diff --git a/drivers/i2c/busses/i2c-pasemi-core.c b/drivers/i2c/busses/i2c-pasemi-core.c
index 9028ffb58cc0..7d54a9f34c74 100644
--- a/drivers/i2c/busses/i2c-pasemi-core.c
+++ b/drivers/i2c/busses/i2c-pasemi-core.c
@@ -21,6 +21,7 @@
#define REG_MTXFIFO 0x00
#define REG_MRXFIFO 0x04
#define REG_SMSTA 0x14
+#define REG_IMASK 0x18
#define REG_CTL 0x1c
#define REG_REV 0x28
@@ -66,6 +67,7 @@ static void pasemi_reset(struct pasemi_smbus *smbus)
val |= CTL_EN;
reg_write(smbus, REG_CTL, val);
+ reinit_completion(&smbus->irq_completion);
}
static void pasemi_smb_clear(struct pasemi_smbus *smbus)
@@ -78,14 +80,21 @@ static void pasemi_smb_clear(struct pasemi_smbus *smbus)
static int pasemi_smb_waitready(struct pasemi_smbus *smbus)
{
- int timeout = 10;
+ int timeout = 100;
unsigned int status;
- status = reg_read(smbus, REG_SMSTA);
-
- while (!(status & SMSTA_XEN) && timeout--) {
- msleep(1);
+ if (smbus->use_irq) {
+ reinit_completion(&smbus->irq_completion);
+ reg_write(smbus, REG_IMASK, SMSTA_XEN | SMSTA_MTN);
+ wait_for_completion_timeout(&smbus->irq_completion, msecs_to_jiffies(100));
+ reg_write(smbus, REG_IMASK, 0);
status = reg_read(smbus, REG_SMSTA);
+ } else {
+ status = reg_read(smbus, REG_SMSTA);
+ while (!(status & SMSTA_XEN) && timeout--) {
+ msleep(1);
+ status = reg_read(smbus, REG_SMSTA);
+ }
}
/* Got NACK? */
@@ -344,10 +353,14 @@ int pasemi_i2c_common_probe(struct pasemi_smbus *smbus)
/* set up the sysfs linkage to our parent device */
smbus->adapter.dev.parent = smbus->dev;
+ smbus->use_irq = 0;
+ init_completion(&smbus->irq_completion);
if (smbus->hw_rev != PASEMI_HW_REV_PCI)
smbus->hw_rev = reg_read(smbus, REG_REV);
+ reg_write(smbus, REG_IMASK, 0);
+
pasemi_reset(smbus);
error = devm_i2c_add_adapter(smbus->dev, &smbus->adapter);
@@ -356,3 +369,12 @@ int pasemi_i2c_common_probe(struct pasemi_smbus *smbus)
return 0;
}
+
+irqreturn_t pasemi_irq_handler(int irq, void *dev_id)
+{
+ struct pasemi_smbus *smbus = dev_id;
+
+ reg_write(smbus, REG_IMASK, 0);
+ complete(&smbus->irq_completion);
+ return IRQ_HANDLED;
+}
diff --git a/drivers/i2c/busses/i2c-pasemi-core.h b/drivers/i2c/busses/i2c-pasemi-core.h
index 4655124a37f3..88821f4e8a9f 100644
--- a/drivers/i2c/busses/i2c-pasemi-core.h
+++ b/drivers/i2c/busses/i2c-pasemi-core.h
@@ -7,6 +7,7 @@
#include <linux/i2c-smbus.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/completion.h>
#define PASEMI_HW_REV_PCI -1
@@ -16,6 +17,10 @@ struct pasemi_smbus {
void __iomem *ioaddr;
unsigned int clk_div;
int hw_rev;
+ int use_irq;
+ struct completion irq_completion;
};
int pasemi_i2c_common_probe(struct pasemi_smbus *smbus);
+
+irqreturn_t pasemi_irq_handler(int irq, void *dev_id);
diff --git a/drivers/i2c/busses/i2c-pasemi-platform.c b/drivers/i2c/busses/i2c-pasemi-platform.c
index 88a54aaf7e3c..e35945a91dbe 100644
--- a/drivers/i2c/busses/i2c-pasemi-platform.c
+++ b/drivers/i2c/busses/i2c-pasemi-platform.c
@@ -49,6 +49,7 @@ static int pasemi_platform_i2c_probe(struct platform_device *pdev)
struct pasemi_smbus *smbus;
u32 frequency;
int error;
+ int irq_num;
data = devm_kzalloc(dev, sizeof(struct pasemi_platform_i2c_data),
GFP_KERNEL);
@@ -82,6 +83,11 @@ static int pasemi_platform_i2c_probe(struct platform_device *pdev)
if (error)
goto out_clk_disable;
+ irq_num = platform_get_irq(pdev, 0);
+ error = devm_request_irq(smbus->dev, irq_num, pasemi_irq_handler, 0, "pasemi_apple_i2c", (void *)smbus);
+
+ if (!error)
+ smbus->use_irq = 1;
platform_set_drvdata(pdev, data);
return 0;