Commit e826192c authored by Arminder Singh's avatar Arminder Singh Committed by Wolfram Sang
Browse files

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: default avatarArminder Singh <arminders208@outlook.com>
Reviewed-by: default avatarSven Peter <sven@svenpeter.dev>
Reviewed-by: default avatarHector Martin <marcan@marcan.st>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 8502bee5
Loading
Loading
Loading
Loading
+27 −5
Original line number Diff line number Diff line
@@ -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,15 +80,22 @@ 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;

	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? */
	if (status & SMSTA_MTN)
@@ -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;
}
+5 −0
Original line number Diff line number Diff line
@@ -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);
+6 −0
Original line number Diff line number Diff line
@@ -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;