Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Arminder Singh and committed by
Wolfram Sang
e826192c 8502bee5

+38 -5
+27 -5
drivers/i2c/busses/i2c-pasemi-core.c
··· 21 21 #define REG_MTXFIFO 0x00 22 22 #define REG_MRXFIFO 0x04 23 23 #define REG_SMSTA 0x14 24 + #define REG_IMASK 0x18 24 25 #define REG_CTL 0x1c 25 26 #define REG_REV 0x28 26 27 ··· 67 66 val |= CTL_EN; 68 67 69 68 reg_write(smbus, REG_CTL, val); 69 + reinit_completion(&smbus->irq_completion); 70 70 } 71 71 72 72 static void pasemi_smb_clear(struct pasemi_smbus *smbus) ··· 80 78 81 79 static int pasemi_smb_waitready(struct pasemi_smbus *smbus) 82 80 { 83 - int timeout = 10; 81 + int timeout = 100; 84 82 unsigned int status; 85 83 86 - status = reg_read(smbus, REG_SMSTA); 87 - 88 - while (!(status & SMSTA_XEN) && timeout--) { 89 - msleep(1); 84 + if (smbus->use_irq) { 85 + reinit_completion(&smbus->irq_completion); 86 + reg_write(smbus, REG_IMASK, SMSTA_XEN | SMSTA_MTN); 87 + wait_for_completion_timeout(&smbus->irq_completion, msecs_to_jiffies(100)); 88 + reg_write(smbus, REG_IMASK, 0); 90 89 status = reg_read(smbus, REG_SMSTA); 90 + } else { 91 + status = reg_read(smbus, REG_SMSTA); 92 + while (!(status & SMSTA_XEN) && timeout--) { 93 + msleep(1); 94 + status = reg_read(smbus, REG_SMSTA); 95 + } 91 96 } 92 97 93 98 /* Got NACK? */ ··· 353 344 354 345 /* set up the sysfs linkage to our parent device */ 355 346 smbus->adapter.dev.parent = smbus->dev; 347 + smbus->use_irq = 0; 348 + init_completion(&smbus->irq_completion); 356 349 357 350 if (smbus->hw_rev != PASEMI_HW_REV_PCI) 358 351 smbus->hw_rev = reg_read(smbus, REG_REV); 352 + 353 + reg_write(smbus, REG_IMASK, 0); 359 354 360 355 pasemi_reset(smbus); 361 356 ··· 368 355 return error; 369 356 370 357 return 0; 358 + } 359 + 360 + irqreturn_t pasemi_irq_handler(int irq, void *dev_id) 361 + { 362 + struct pasemi_smbus *smbus = dev_id; 363 + 364 + reg_write(smbus, REG_IMASK, 0); 365 + complete(&smbus->irq_completion); 366 + return IRQ_HANDLED; 371 367 }
+5
drivers/i2c/busses/i2c-pasemi-core.h
··· 7 7 #include <linux/i2c-smbus.h> 8 8 #include <linux/io.h> 9 9 #include <linux/kernel.h> 10 + #include <linux/completion.h> 10 11 11 12 #define PASEMI_HW_REV_PCI -1 12 13 ··· 17 16 void __iomem *ioaddr; 18 17 unsigned int clk_div; 19 18 int hw_rev; 19 + int use_irq; 20 + struct completion irq_completion; 20 21 }; 21 22 22 23 int pasemi_i2c_common_probe(struct pasemi_smbus *smbus); 24 + 25 + irqreturn_t pasemi_irq_handler(int irq, void *dev_id);
+6
drivers/i2c/busses/i2c-pasemi-platform.c
··· 49 49 struct pasemi_smbus *smbus; 50 50 u32 frequency; 51 51 int error; 52 + int irq_num; 52 53 53 54 data = devm_kzalloc(dev, sizeof(struct pasemi_platform_i2c_data), 54 55 GFP_KERNEL); ··· 83 82 if (error) 84 83 goto out_clk_disable; 85 84 85 + irq_num = platform_get_irq(pdev, 0); 86 + error = devm_request_irq(smbus->dev, irq_num, pasemi_irq_handler, 0, "pasemi_apple_i2c", (void *)smbus); 87 + 88 + if (!error) 89 + smbus->use_irq = 1; 86 90 platform_set_drvdata(pdev, data); 87 91 88 92 return 0;