From a263a84088f689bf0c1552a510b25d0bcc45fcae Mon Sep 17 00:00:00 2001 From: Akhil R Date: Fri, 28 Jan 2022 17:14:27 +0530 Subject: i2c: smbus: Use device_*() functions instead of of_*() Change of_*() functions to device_*() for firmware agnostic usage. This allows to have the smbus_alert interrupt without any changes in the controller drivers using the ACPI table. Signed-off-by: Akhil R Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 2 +- drivers/i2c/i2c-core-smbus.c | 11 ++++++----- drivers/i2c/i2c-smbus.c | 5 +++-- 3 files changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 2c59dd748a49..32a45260c1f3 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1479,7 +1479,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap) goto out_list; } - res = of_i2c_setup_smbus_alert(adap); + res = i2c_setup_smbus_alert(adap); if (res) goto out_reg; diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index e5b2d1465e7e..304c2c8fee68 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "i2c-core.h" @@ -701,13 +702,13 @@ struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter, } EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device); -#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF) -int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter) +#if IS_ENABLED(CONFIG_I2C_SMBUS) +int i2c_setup_smbus_alert(struct i2c_adapter *adapter) { int irq; - irq = of_property_match_string(adapter->dev.of_node, "interrupt-names", - "smbus_alert"); + irq = device_property_match_string(adapter->dev.parent, "interrupt-names", + "smbus_alert"); if (irq == -EINVAL || irq == -ENODATA) return 0; else if (irq < 0) @@ -715,5 +716,5 @@ int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter) return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL)); } -EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert); +EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert); #endif diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index d3d06e3b4f3b..775332945ad0 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include @@ -128,7 +128,8 @@ static int smbalert_probe(struct i2c_client *ara, if (setup) { irq = setup->irq; } else { - irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert"); + irq = fwnode_irq_get_byname(dev_fwnode(adapter->dev.parent), + "smbus_alert"); if (irq <= 0) return irq; } -- cgit v1.2.3 From 934705316f0fce48e0a944e6efb3f8da8c616b1c Mon Sep 17 00:00:00 2001 From: Kewei Xu Date: Tue, 25 Jan 2022 19:04:13 +0800 Subject: i2c: mediatek: Add i2c compatible for Mediatek MT8186 Add i2c compatible for MT8186. Compare to MT8192 i2c controller, MT8186 doesn't need handshake signal witch apdma. Signed-off-by: Kewei Xu Reviewed-by: Qii Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 9ea427f53083..aa4d21838e14 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -397,6 +397,19 @@ static const struct mtk_i2c_compatible mt8183_compat = { .max_dma_support = 33, }; +static const struct mtk_i2c_compatible mt8186_compat = { + .regs = mt_i2c_regs_v2, + .pmic_i2c = 0, + .dcm = 0, + .auto_restart = 1, + .aux_len_reg = 1, + .timing_adjust = 1, + .dma_sync = 0, + .ltiming_adjust = 1, + .apdma_sync = 0, + .max_dma_support = 36, +}; + static const struct mtk_i2c_compatible mt8192_compat = { .quirks = &mt8183_i2c_quirks, .regs = mt_i2c_regs_v2, @@ -418,6 +431,7 @@ static const struct of_device_id mtk_i2c_of_match[] = { { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, + { .compatible = "mediatek,mt8186-i2c", .data = &mt8186_compat }, { .compatible = "mediatek,mt8192-i2c", .data = &mt8192_compat }, {} }; -- cgit v1.2.3 From 951285e59e8ac1d056780b3b5f024cdbdc9846d8 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 9 Jan 2022 13:34:18 +0100 Subject: i2c: amd-mp2: Remove useless DMA-32 fallback configuration As stated in [1], dma_set_mask() with a 64-bit mask never fails if dev->dma_mask is non-NULL. So, if it fails, the 32 bits case will also fail for the same reason. Simplify code and remove some dead code accordingly. [1]: https://lkml.org/lkml/2021/6/7/398 Signed-off-by: Christophe JAILLET Acked-by: Nehal Bakulchandra Shah Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-amd-mp2-pci.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-amd-mp2-pci.c b/drivers/i2c/busses/i2c-amd-mp2-pci.c index adf0e8c1ec01..f57077a7448d 100644 --- a/drivers/i2c/busses/i2c-amd-mp2-pci.c +++ b/drivers/i2c/busses/i2c-amd-mp2-pci.c @@ -308,11 +308,8 @@ static int amd_mp2_pci_init(struct amd_mp2_dev *privdata, pci_set_master(pci_dev); rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(64)); - if (rc) { - rc = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32)); - if (rc) - goto err_dma_mask; - } + if (rc) + goto err_dma_mask; /* Set up intx irq */ writel(0, privdata->mmio + AMD_P2C_MSG_INTEN); -- cgit v1.2.3 From b205f5850263632b6897d8f0bfaeeea4955f8663 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 20 Aug 2021 20:43:33 +0200 Subject: i2c: bcm2835: Fix the error handling in 'bcm2835_i2c_probe()' Some resource should be released if an error occurs in 'bcm2835_i2c_probe()'. Add an error handling path and the needed 'clk_disable_unprepare()' and 'clk_rate_exclusive_put()' calls. While at it, rework the bottom of the function to use this newly added error handling path and have an explicit and more standard "return 0;" at the end of the normal path. Fixes: bebff81fb8b9 ("i2c: bcm2835: Model Divider in CCF") Signed-off-by: Christophe JAILLET [wsa: rebased] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-bcm2835.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c index dfc534065595..f2dc6616afdd 100644 --- a/drivers/i2c/busses/i2c-bcm2835.c +++ b/drivers/i2c/busses/i2c-bcm2835.c @@ -449,18 +449,20 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) ret = clk_prepare_enable(i2c_dev->bus_clk); if (ret) { dev_err(&pdev->dev, "Couldn't prepare clock"); - return ret; + goto err_put_exclusive_rate; } i2c_dev->irq = platform_get_irq(pdev, 0); - if (i2c_dev->irq < 0) - return i2c_dev->irq; + if (i2c_dev->irq < 0) { + ret = i2c_dev->irq; + goto err_disable_unprepare_clk; + } ret = request_irq(i2c_dev->irq, bcm2835_i2c_isr, IRQF_SHARED, dev_name(&pdev->dev), i2c_dev); if (ret) { dev_err(&pdev->dev, "Could not request IRQ\n"); - return -ENODEV; + goto err_disable_unprepare_clk; } adap = &i2c_dev->adapter; @@ -478,7 +480,16 @@ static int bcm2835_i2c_probe(struct platform_device *pdev) ret = i2c_add_adapter(adap); if (ret) - free_irq(i2c_dev->irq, i2c_dev); + goto err_free_irq; + + return 0; + +err_free_irq: + free_irq(i2c_dev->irq, i2c_dev); +err_disable_unprepare_clk: + clk_disable_unprepare(i2c_dev->bus_clk); +err_put_exclusive_rate: + clk_rate_exclusive_put(i2c_dev->bus_clk); return ret; } -- cgit v1.2.3 From 0bc4978a679c4efe37c6919cb2082dc123d40120 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 1 Feb 2022 19:00:42 +0100 Subject: i2c: ACPI: Replace acpi_bus_get_device() Replace acpi_bus_get_device() that is going to be dropped with acpi_fetch_acpi_dev(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Acked-by: Mika Westerberg Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-acpi.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 85ed4c1d4924..08b561f0709d 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -236,7 +236,8 @@ static int i2c_acpi_get_info(struct acpi_device *adev, struct acpi_device *adapter_adev; /* The adapter must be present */ - if (acpi_bus_get_device(lookup.adapter_handle, &adapter_adev)) + adapter_adev = acpi_fetch_acpi_dev(lookup.adapter_handle); + if (!adapter_adev) return -ENODEV; if (acpi_bus_get_status(adapter_adev) || !adapter_adev->status.present) @@ -275,13 +276,10 @@ static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, void *data, void **return_value) { struct i2c_adapter *adapter = data; - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); struct i2c_board_info info; - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - - if (i2c_acpi_get_info(adev, &info, adapter, NULL)) + if (!adev || i2c_acpi_get_info(adev, &info, adapter, NULL)) return AE_OK; i2c_acpi_register_device(adapter, adev, &info); @@ -341,12 +339,9 @@ static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level, void *data, void **return_value) { struct i2c_acpi_lookup *lookup = data; - struct acpi_device *adev; - - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); - if (i2c_acpi_do_lookup(adev, lookup)) + if (!adev || i2c_acpi_do_lookup(adev, lookup)) return AE_OK; if (lookup->search_handle != lookup->adapter_handle) -- cgit v1.2.3 From 0c47dd7d09bb5de5a8e5de24c7e04d66e42a727e Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Sat, 29 Jan 2022 10:39:07 +0100 Subject: i2c: npcm7xx: Fix typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comments in this driver have a few typos. Let's fix them. Signed-off-by: Jonathan Neuschäfer Reviewed-by: Randy Dunlap Reviewed-by: tali.perry@nuvoton.com Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-npcm7xx.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-npcm7xx.c b/drivers/i2c/busses/i2c-npcm7xx.c index 2ad166355ec9..71aad029425d 100644 --- a/drivers/i2c/busses/i2c-npcm7xx.c +++ b/drivers/i2c/busses/i2c-npcm7xx.c @@ -781,7 +781,7 @@ static void npcm_i2c_set_fifo(struct npcm_i2c *bus, int nread, int nwrite) /* * if we are about to read the first byte in blk rd mode, * don't NACK it. If slave returns zero size HW can't NACK - * it immidiattly, it will read extra byte and then NACK. + * it immediately, it will read extra byte and then NACK. */ if (bus->rd_ind == 0 && bus->read_block_use) { /* set fifo to read one byte, no last: */ @@ -981,7 +981,7 @@ static void npcm_i2c_slave_xmit(struct npcm_i2c *bus, u16 nwrite, /* * npcm_i2c_slave_wr_buf_sync: * currently slave IF only supports single byte operations. - * in order to utilyze the npcm HW FIFO, the driver will ask for 16 bytes + * in order to utilize the npcm HW FIFO, the driver will ask for 16 bytes * at a time, pack them in buffer, and then transmit them all together * to the FIFO and onward to the bus. * NACK on read will be once reached to bus->adap->quirks->max_read_len. @@ -1175,7 +1175,7 @@ static irqreturn_t npcm_i2c_int_slave_handler(struct npcm_i2c *bus) /* * the i2c module can response to 10 own SA. * check which one was addressed by the master. - * repond to the first one. + * respond to the first one. */ addr = ((i2ccst3 & 0x07) << 7) | (i2ccst2 & 0x7F); @@ -1753,8 +1753,8 @@ static void npcm_i2c_recovery_init(struct i2c_adapter *_adap) /* * npcm i2c HW allows direct reading of SCL and SDA. * However, it does not support setting SCL and SDA directly. - * The recovery function can togle SCL when SDA is low (but not set) - * Getter functions used internally, and can be used externaly. + * The recovery function can toggle SCL when SDA is low (but not set) + * Getter functions used internally, and can be used externally. */ rinfo->get_scl = npcm_i2c_get_SCL; rinfo->get_sda = npcm_i2c_get_SDA; @@ -1768,10 +1768,10 @@ static void npcm_i2c_recovery_init(struct i2c_adapter *_adap) /* * npcm_i2c_init_clk: init HW timing parameters. - * NPCM7XX i2c module timing parameters are depenent on module core clk (APB) + * NPCM7XX i2c module timing parameters are dependent on module core clk (APB) * and bus frequency. - * 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are simetric. - * 400kHz bus requires assymetric HT and LT. A different equation is recomended + * 100kHz bus requires tSCL = 4 * SCLFRQ * tCLK. LT and HT are symmetric. + * 400kHz bus requires asymmetric HT and LT. A different equation is recommended * by the HW designer, given core clock range (equations in comments below). * */ -- cgit v1.2.3 From 93102cb449780f7b4eecf713451627b78373ce49 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Wed, 9 Feb 2022 11:27:10 -0600 Subject: i2c: piix4: Replace hardcoded memory map size with a #define Replace number constant with #define to improve readability and maintainability. Signed-off-by: Terry Bowman Reviewed-by: Andy Shevchenko Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-piix4.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 8c1b31ed0c42..3ff68967034e 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -77,6 +77,7 @@ /* SB800 constants */ #define SB800_PIIX4_SMB_IDX 0xcd6 +#define SB800_PIIX4_SMB_MAP_SIZE 2 #define KERNCZ_IMC_IDX 0x3e #define KERNCZ_IMC_DATA 0x3f @@ -290,7 +291,8 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, else smb_en = (aux) ? 0x28 : 0x2c; - if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) { + if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE, + "sb800_piix4_smb")) { dev_err(&PIIX4_dev->dev, "SMB base address index region 0x%x already in use.\n", SB800_PIIX4_SMB_IDX); @@ -302,7 +304,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); - release_region(SB800_PIIX4_SMB_IDX, 2); + release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); if (!smb_en) { smb_en_status = smba_en_lo & 0x10; @@ -371,7 +373,8 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; } } else { - if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, + if (!request_muxed_region(SB800_PIIX4_SMB_IDX, + SB800_PIIX4_SMB_MAP_SIZE, "sb800_piix4_smb")) { release_region(piix4_smba, SMBIOSIZE); return -EBUSY; @@ -384,7 +387,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, SB800_PIIX4_PORT_IDX; piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; - release_region(SB800_PIIX4_SMB_IDX, 2); + release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); } dev_info(&PIIX4_dev->dev, @@ -682,7 +685,8 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, u8 port; int retval; - if (!request_muxed_region(SB800_PIIX4_SMB_IDX, 2, "sb800_piix4_smb")) + if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE, + "sb800_piix4_smb")) return -EBUSY; /* Request the SMBUS semaphore, avoid conflicts with the IMC */ @@ -758,7 +762,7 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, piix4_imc_wakeup(); release: - release_region(SB800_PIIX4_SMB_IDX, 2); + release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); return retval; } -- cgit v1.2.3 From a3325d225b00889f4b7fdb25d83033cae1048a92 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Wed, 9 Feb 2022 11:27:11 -0600 Subject: i2c: piix4: Move port I/O region request/release code into functions Move duplicated region request and release code into a function. Move is in preparation for following MMIO changes. Signed-off-by: Terry Bowman Reviewed-by: Andy Shevchenko Reviewed-by: Jean Delvare [wsa: added missing curly brace] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-piix4.c | 48 +++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 17 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 3ff68967034e..39c4f5ce0cd9 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -165,6 +165,24 @@ struct i2c_piix4_adapdata { u8 port; /* Port number, shifted */ }; +static int piix4_sb800_region_request(struct device *dev) +{ + if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE, + "sb800_piix4_smb")) { + dev_err(dev, + "SMBus base address index region 0x%x already in use.\n", + SB800_PIIX4_SMB_IDX); + return -EBUSY; + } + + return 0; +} + +static void piix4_sb800_region_release(struct device *dev) +{ + release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); +} + static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id) { @@ -270,6 +288,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, unsigned short piix4_smba; u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status, port_sel; u8 i2ccfg, i2ccfg_offset = 0x10; + int retval; /* SB800 and later SMBus does not support forcing address */ if (force || force_addr) { @@ -291,20 +310,16 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, else smb_en = (aux) ? 0x28 : 0x2c; - if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE, - "sb800_piix4_smb")) { - dev_err(&PIIX4_dev->dev, - "SMB base address index region 0x%x already in use.\n", - SB800_PIIX4_SMB_IDX); - return -EBUSY; - } + retval = piix4_sb800_region_request(&PIIX4_dev->dev); + if (retval) + return retval; outb_p(smb_en, SB800_PIIX4_SMB_IDX); smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); - release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); + piix4_sb800_region_release(&PIIX4_dev->dev); if (!smb_en) { smb_en_status = smba_en_lo & 0x10; @@ -373,11 +388,10 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; } } else { - if (!request_muxed_region(SB800_PIIX4_SMB_IDX, - SB800_PIIX4_SMB_MAP_SIZE, - "sb800_piix4_smb")) { + retval = piix4_sb800_region_request(&PIIX4_dev->dev); + if (retval) { release_region(piix4_smba, SMBIOSIZE); - return -EBUSY; + return retval; } outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX); @@ -387,7 +401,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, SB800_PIIX4_PORT_IDX; piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; - release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); + piix4_sb800_region_release(&PIIX4_dev->dev); } dev_info(&PIIX4_dev->dev, @@ -685,9 +699,9 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, u8 port; int retval; - if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE, - "sb800_piix4_smb")) - return -EBUSY; + retval = piix4_sb800_region_request(&adap->dev); + if (retval) + return retval; /* Request the SMBUS semaphore, avoid conflicts with the IMC */ smbslvcnt = inb_p(SMBSLVCNT); @@ -762,7 +776,7 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, piix4_imc_wakeup(); release: - release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); + piix4_sb800_region_release(&adap->dev); return retval; } -- cgit v1.2.3 From 0a59a24e14e9b21dcbb6b8ea41422e2fdfa437fd Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Wed, 9 Feb 2022 11:27:12 -0600 Subject: i2c: piix4: Move SMBus controller base address detect into function Move SMBus controller base address detection into function. Refactor is in preparation for following MMIO changes. Signed-off-by: Terry Bowman Reviewed-by: Andy Shevchenko Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-piix4.c | 69 +++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 25 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 39c4f5ce0cd9..132ded733781 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -282,11 +282,51 @@ static int piix4_setup(struct pci_dev *PIIX4_dev, return piix4_smba; } +static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev, + u8 smb_en, + u8 aux, + u8 *smb_en_status, + unsigned short *piix4_smba) +{ + u8 smba_en_lo; + u8 smba_en_hi; + int retval; + + retval = piix4_sb800_region_request(&PIIX4_dev->dev); + if (retval) + return retval; + + outb_p(smb_en, SB800_PIIX4_SMB_IDX); + smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); + outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); + smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); + + piix4_sb800_region_release(&PIIX4_dev->dev); + + if (!smb_en) { + *smb_en_status = smba_en_lo & 0x10; + *piix4_smba = smba_en_hi << 8; + if (aux) + *piix4_smba |= 0x20; + } else { + *smb_en_status = smba_en_lo & 0x01; + *piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; + } + + if (!*smb_en_status) { + dev_err(&PIIX4_dev->dev, + "SMBus Host Controller not enabled!\n"); + return -ENODEV; + } + + return 0; +} + static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, const struct pci_device_id *id, u8 aux) { unsigned short piix4_smba; - u8 smba_en_lo, smba_en_hi, smb_en, smb_en_status, port_sel; + u8 smb_en, smb_en_status, port_sel; u8 i2ccfg, i2ccfg_offset = 0x10; int retval; @@ -310,33 +350,12 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, else smb_en = (aux) ? 0x28 : 0x2c; - retval = piix4_sb800_region_request(&PIIX4_dev->dev); + retval = piix4_setup_sb800_smba(PIIX4_dev, smb_en, aux, &smb_en_status, + &piix4_smba); + if (retval) return retval; - outb_p(smb_en, SB800_PIIX4_SMB_IDX); - smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); - outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); - smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); - - piix4_sb800_region_release(&PIIX4_dev->dev); - - if (!smb_en) { - smb_en_status = smba_en_lo & 0x10; - piix4_smba = smba_en_hi << 8; - if (aux) - piix4_smba |= 0x20; - } else { - smb_en_status = smba_en_lo & 0x01; - piix4_smba = ((smba_en_hi << 8) | smba_en_lo) & 0xffe0; - } - - if (!smb_en_status) { - dev_err(&PIIX4_dev->dev, - "SMBus Host Controller not enabled!\n"); - return -ENODEV; - } - if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) return -ENODEV; -- cgit v1.2.3 From fbafbd51bff52cb3a920fd98d4dae2a78dd433d0 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Wed, 9 Feb 2022 11:27:13 -0600 Subject: i2c: piix4: Move SMBus port selection into function Move port selection code into a separate function. Refactor is in preparation for following MMIO changes. Signed-off-by: Terry Bowman Reviewed-by: Andy Shevchenko Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-piix4.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 132ded733781..efe51f67fdd6 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -698,6 +698,20 @@ static void piix4_imc_wakeup(void) release_region(KERNCZ_IMC_IDX, 2); } +static int piix4_sb800_port_sel(u8 port) +{ + u8 smba_en_lo, val; + + outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); + smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); + + val = (smba_en_lo & ~piix4_port_mask_sb800) | port; + if (smba_en_lo != val) + outb_p(val, SB800_PIIX4_SMB_IDX + 1); + + return (smba_en_lo & piix4_port_mask_sb800); +} + /* * Handles access to multiple SMBus ports on the SB800. * The port is selected by bits 2:1 of the smb_en register (0x2c). @@ -714,8 +728,7 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, unsigned short piix4_smba = adapdata->smba; int retries = MAX_TIMEOUT; int smbslvcnt; - u8 smba_en_lo; - u8 port; + u8 prev_port; int retval; retval = piix4_sb800_region_request(&adap->dev); @@ -775,18 +788,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, } } - outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); - smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); - - port = adapdata->port; - if ((smba_en_lo & piix4_port_mask_sb800) != port) - outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port, - SB800_PIIX4_SMB_IDX + 1); + prev_port = piix4_sb800_port_sel(adapdata->port); retval = piix4_access(adap, addr, flags, read_write, command, size, data); - outb_p(smba_en_lo, SB800_PIIX4_SMB_IDX + 1); + piix4_sb800_port_sel(prev_port); /* Release the semaphore */ outb_p(smbslvcnt | 0x20, SMBSLVCNT); -- cgit v1.2.3 From 7c148722d074c29fb998578eea5de3c14b9608c9 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Wed, 9 Feb 2022 11:27:14 -0600 Subject: i2c: piix4: Add EFCH MMIO support to region request and release EFCH cd6h/cd7h port I/O may no longer be available on later AMD processors and it is recommended to use MMIO instead. Update the request and release functions to support MMIO. MMIO request/release and mmapping require details during cleanup. Add a MMIO configuration structure containing resource and vaddress details for mapping the region, accessing the region, and releasing the region. Signed-off-by: Terry Bowman Reviewed-by: Andy Shevchenko Reviewed-by: Jean Delvare [wsa: rebased after fixup in previous patch] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-piix4.c | 66 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 8 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index efe51f67fdd6..d06998f7b031 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -98,6 +98,9 @@ #define SB800_PIIX4_PORT_IDX_MASK_KERNCZ 0x18 #define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ 3 +#define SB800_PIIX4_FCH_PM_ADDR 0xFED80300 +#define SB800_PIIX4_FCH_PM_SIZE 8 + /* insmod parameters */ /* If force is set to anything different from 0, we forcibly enable the @@ -156,6 +159,12 @@ static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = { }; static const char *piix4_aux_port_name_sb800 = " port 1"; +struct sb800_mmio_cfg { + void __iomem *addr; + struct resource *res; + bool use_mmio; +}; + struct i2c_piix4_adapdata { unsigned short smba; @@ -163,10 +172,40 @@ struct i2c_piix4_adapdata { bool sb800_main; bool notify_imc; u8 port; /* Port number, shifted */ + struct sb800_mmio_cfg mmio_cfg; }; -static int piix4_sb800_region_request(struct device *dev) +static int piix4_sb800_region_request(struct device *dev, + struct sb800_mmio_cfg *mmio_cfg) { + if (mmio_cfg->use_mmio) { + struct resource *res; + void __iomem *addr; + + res = request_mem_region_muxed(SB800_PIIX4_FCH_PM_ADDR, + SB800_PIIX4_FCH_PM_SIZE, + "sb800_piix4_smb"); + if (!res) { + dev_err(dev, + "SMBus base address memory region 0x%x already in use.\n", + SB800_PIIX4_FCH_PM_ADDR); + return -EBUSY; + } + + addr = ioremap(SB800_PIIX4_FCH_PM_ADDR, + SB800_PIIX4_FCH_PM_SIZE); + if (!addr) { + release_resource(res); + dev_err(dev, "SMBus base address mapping failed.\n"); + return -ENOMEM; + } + + mmio_cfg->res = res; + mmio_cfg->addr = addr; + + return 0; + } + if (!request_muxed_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE, "sb800_piix4_smb")) { dev_err(dev, @@ -178,8 +217,15 @@ static int piix4_sb800_region_request(struct device *dev) return 0; } -static void piix4_sb800_region_release(struct device *dev) +static void piix4_sb800_region_release(struct device *dev, + struct sb800_mmio_cfg *mmio_cfg) { + if (mmio_cfg->use_mmio) { + iounmap(mmio_cfg->addr); + release_resource(mmio_cfg->res); + return; + } + release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); } @@ -288,11 +334,13 @@ static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev, u8 *smb_en_status, unsigned short *piix4_smba) { + struct sb800_mmio_cfg mmio_cfg; u8 smba_en_lo; u8 smba_en_hi; int retval; - retval = piix4_sb800_region_request(&PIIX4_dev->dev); + mmio_cfg.use_mmio = 0; + retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg); if (retval) return retval; @@ -301,7 +349,7 @@ static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev, outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); - piix4_sb800_region_release(&PIIX4_dev->dev); + piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg); if (!smb_en) { *smb_en_status = smba_en_lo & 0x10; @@ -328,6 +376,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, unsigned short piix4_smba; u8 smb_en, smb_en_status, port_sel; u8 i2ccfg, i2ccfg_offset = 0x10; + struct sb800_mmio_cfg mmio_cfg; int retval; /* SB800 and later SMBus does not support forcing address */ @@ -407,7 +456,8 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; } } else { - retval = piix4_sb800_region_request(&PIIX4_dev->dev); + mmio_cfg.use_mmio = 0; + retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg); if (retval) { release_region(piix4_smba, SMBIOSIZE); return retval; @@ -420,7 +470,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, SB800_PIIX4_PORT_IDX; piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK; piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; - piix4_sb800_region_release(&PIIX4_dev->dev); + piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg); } dev_info(&PIIX4_dev->dev, @@ -731,7 +781,7 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, u8 prev_port; int retval; - retval = piix4_sb800_region_request(&adap->dev); + retval = piix4_sb800_region_request(&adap->dev, &adapdata->mmio_cfg); if (retval) return retval; @@ -802,7 +852,7 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, piix4_imc_wakeup(); release: - piix4_sb800_region_release(&adap->dev); + piix4_sb800_region_release(&adap->dev, &adapdata->mmio_cfg); return retval; } -- cgit v1.2.3 From 46967bc1ee93acd1d8953c87dc16f43de4076f93 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Wed, 9 Feb 2022 11:27:15 -0600 Subject: i2c: piix4: Add EFCH MMIO support to SMBus base address detect The EFCH SMBus controller's base address is determined using details in FCH::PM::DECODEEN[smbusasfiobase] and FCH::PM::DECODEEN[smbusasfioen].These register fields were accessed using cd6h/cd7h port I/O. cd6h/cd7h port I/O is no longer available in later AMD processors. Change base address detection to use MMIO instead of port I/O cd6h/cd7h. Signed-off-by: Terry Bowman Reviewed-by: Andy Shevchenko Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-piix4.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index d06998f7b031..7f312177eb27 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -344,10 +344,15 @@ static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev, if (retval) return retval; - outb_p(smb_en, SB800_PIIX4_SMB_IDX); - smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); - outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); - smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); + if (mmio_cfg.use_mmio) { + smba_en_lo = ioread8(mmio_cfg.addr); + smba_en_hi = ioread8(mmio_cfg.addr + 1); + } else { + outb_p(smb_en, SB800_PIIX4_SMB_IDX); + smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); + outb_p(smb_en + 1, SB800_PIIX4_SMB_IDX); + smba_en_hi = inb_p(SB800_PIIX4_SMB_IDX + 1); + } piix4_sb800_region_release(&PIIX4_dev->dev, &mmio_cfg); -- cgit v1.2.3 From 381a3083c6747ae5cdbef9b176d57d1b966db49f Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Wed, 9 Feb 2022 11:27:16 -0600 Subject: i2c: piix4: Add EFCH MMIO support for SMBus port select AMD processors include registers capable of selecting between 2 SMBus ports. Port selection is made during each user access by writing to FCH::PM::DECODEEN[smbus0sel]. Change the driver to use MMIO during SMBus port selection because cd6h/cd7h port I/O is not available on later AMD processors. Signed-off-by: Terry Bowman Reviewed-by: Andy Shevchenko Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-piix4.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 7f312177eb27..4789fc9ad270 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -753,10 +753,19 @@ static void piix4_imc_wakeup(void) release_region(KERNCZ_IMC_IDX, 2); } -static int piix4_sb800_port_sel(u8 port) +static int piix4_sb800_port_sel(u8 port, struct sb800_mmio_cfg *mmio_cfg) { u8 smba_en_lo, val; + if (mmio_cfg->use_mmio) { + smba_en_lo = ioread8(mmio_cfg->addr + piix4_port_sel_sb800); + val = (smba_en_lo & ~piix4_port_mask_sb800) | port; + if (smba_en_lo != val) + iowrite8(val, mmio_cfg->addr + piix4_port_sel_sb800); + + return (smba_en_lo & piix4_port_mask_sb800); + } + outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1); @@ -843,12 +852,12 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, } } - prev_port = piix4_sb800_port_sel(adapdata->port); + prev_port = piix4_sb800_port_sel(adapdata->port, &adapdata->mmio_cfg); retval = piix4_access(adap, addr, flags, read_write, command, size, data); - piix4_sb800_port_sel(prev_port); + piix4_sb800_port_sel(prev_port, &adapdata->mmio_cfg); /* Release the semaphore */ outb_p(smbslvcnt | 0x20, SMBSLVCNT); -- cgit v1.2.3 From 6cf72f41808ab5db1d7718b999b3ff0166e67e45 Mon Sep 17 00:00:00 2001 From: Terry Bowman Date: Wed, 9 Feb 2022 11:27:17 -0600 Subject: i2c: piix4: Enable EFCH MMIO for Family 17h+ Enable EFCH MMIO using check for SMBus PCI revision ID value 0x51 or greater. This PCI revision ID check will enable family 17h and future AMD processors with the same EFCH SMBus controller HW. Signed-off-by: Terry Bowman Reviewed-by: Andy Shevchenko Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-piix4.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 4789fc9ad270..ac8e7d60672a 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -229,6 +229,18 @@ static void piix4_sb800_region_release(struct device *dev, release_region(SB800_PIIX4_SMB_IDX, SB800_PIIX4_SMB_MAP_SIZE); } +static bool piix4_sb800_use_mmio(struct pci_dev *PIIX4_dev) +{ + /* + * cd6h/cd7h port I/O accesses can be disabled on AMD processors + * w/ SMBus PCI revision ID 0x51 or greater. MMIO is supported on + * the same processors and is the recommended access method. + */ + return (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && + PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && + PIIX4_dev->revision >= 0x51); +} + static int piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id) { @@ -339,7 +351,7 @@ static int piix4_setup_sb800_smba(struct pci_dev *PIIX4_dev, u8 smba_en_hi; int retval; - mmio_cfg.use_mmio = 0; + mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev); retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg); if (retval) return retval; @@ -461,7 +473,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT; } } else { - mmio_cfg.use_mmio = 0; + mmio_cfg.use_mmio = piix4_sb800_use_mmio(PIIX4_dev); retval = piix4_sb800_region_request(&PIIX4_dev->dev, &mmio_cfg); if (retval) { release_region(piix4_smba, SMBIOSIZE); @@ -944,6 +956,7 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, return -ENOMEM; } + adapdata->mmio_cfg.use_mmio = piix4_sb800_use_mmio(dev); adapdata->smba = smba; adapdata->sb800_main = sb800_main; adapdata->port = port << piix4_port_shift_sb800; -- cgit v1.2.3 From 6960331dee17ee54d1b6188f0f4fc9537279d514 Mon Sep 17 00:00:00 2001 From: Jan Dabros Date: Tue, 8 Feb 2022 15:12:17 +0100 Subject: i2c: designware: Add missing locks All accesses to controller's registers should be protected on probe, disable and xfer paths. This is needed for i2c bus controllers that are shared with but not controller by kernel. Signed-off-by: Jan Dabros Reviewed-by: Hans de Goede Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-common.c | 12 ++++++++++++ drivers/i2c/busses/i2c-designware-master.c | 6 ++++++ 2 files changed, 18 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c index bf2a4920638a..9f8574320eb2 100644 --- a/drivers/i2c/busses/i2c-designware-common.c +++ b/drivers/i2c/busses/i2c-designware-common.c @@ -578,7 +578,12 @@ int i2c_dw_set_fifo_size(struct dw_i2c_dev *dev) * Try to detect the FIFO depth if not set by interface driver, * the depth could be from 2 to 256 from HW spec. */ + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, ¶m); + i2c_dw_release_lock(dev); if (ret) return ret; @@ -607,6 +612,11 @@ u32 i2c_dw_func(struct i2c_adapter *adap) void i2c_dw_disable(struct dw_i2c_dev *dev) { u32 dummy; + int ret; + + ret = i2c_dw_acquire_lock(dev); + if (ret) + return; /* Disable controller */ __i2c_dw_disable(dev); @@ -614,6 +624,8 @@ void i2c_dw_disable(struct dw_i2c_dev *dev) /* Disable all interrupts */ regmap_write(dev->map, DW_IC_INTR_MASK, 0); regmap_read(dev->map, DW_IC_CLR_INTR, &dummy); + + i2c_dw_release_lock(dev); } void i2c_dw_disable_int(struct dw_i2c_dev *dev) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 9177463c2cbb..1a4b23556db3 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -905,7 +905,13 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev) irq_flags = IRQF_SHARED | IRQF_COND_SUSPEND; } + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; + i2c_dw_disable_int(dev); + i2c_dw_release_lock(dev); + ret = devm_request_irq(dev->dev, dev->irq, i2c_dw_isr, irq_flags, dev_name(dev->dev), dev); if (ret) { -- cgit v1.2.3 From 78d5e9e299e31bc2deaaa94a45bf8ea024f27e8c Mon Sep 17 00:00:00 2001 From: Jan Dabros Date: Tue, 8 Feb 2022 15:12:18 +0100 Subject: i2c: designware: Add AMD PSP I2C bus support Implement an I2C controller sharing mechanism between the host (kernel) and PSP co-processor on some platforms equipped with AMD Cezanne SoC. On these platforms we need to implement "software" i2c arbitration. Default arbitration owner is PSP and kernel asks for acquire as well as inform about release of the i2c bus via mailbox mechanism. +---------+ <- ACQUIRE | | +---------| CPU |\ | | | \ +----------+ SDA | +---------+ \ | |------- MAILBOX +--> | I2C-DW | SCL | +---------+ | |------- | | | +----------+ +---------| PSP | <- ACK | | +---------+ +---------+ <- RELEASE | | +---------| CPU | | | | +----------+ SDA | +---------+ | |------- MAILBOX +--> | I2C-DW | SCL | +---------+ / | |------- | | | / +----------+ +---------| PSP |/ <- ACK | | +---------+ The solution is similar to i2c-designware-baytrail.c implementation, where we are using a generic i2c-designware-* driver with a small "wrapper". In contrary to baytrail semaphore implementation, beside internal acquire_lock() and release_lock() methods we are also applying quirks to lock_bus() and unlock_bus() global adapter methods. With this in place all i2c clients drivers may lock i2c bus for a desired number of i2c transactions (e.g. write-wait-read) without being aware of that such bus is shared with another entity. Modify i2c_dw_probe_lock_support() to select correct semaphore implementation at runtime, since now we have more than one available. Configure new matching ACPI ID "AMDI0019" and register ARBITRATION_SEMAPHORE flag in order to distinguish setup with PSP arbitration. Add myself as a reviewer for I2C DesignWare in order to help with reviewing and testing possible changes touching new i2c-designware-amdpsp.c module. Signed-off-by: Jan Dabros Reviewed-by: Andy Shevchenko Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula [wsa: removed unneeded blank line and curly braces] Signed-off-by: Wolfram Sang --- MAINTAINERS | 1 + drivers/acpi/acpi_apd.c | 7 +- drivers/i2c/busses/Kconfig | 11 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-designware-amdpsp.c | 392 +++++++++++++++++++++++++++ drivers/i2c/busses/i2c-designware-baytrail.c | 12 +- drivers/i2c/busses/i2c-designware-core.h | 18 +- drivers/i2c/busses/i2c-designware-platdrv.c | 60 ++++ 8 files changed, 490 insertions(+), 12 deletions(-) create mode 100644 drivers/i2c/busses/i2c-designware-amdpsp.c (limited to 'drivers/i2c') diff --git a/MAINTAINERS b/MAINTAINERS index ea3e6c914384..c2e76d94cc18 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18660,6 +18660,7 @@ SYNOPSYS DESIGNWARE I2C DRIVER M: Jarkko Nikula R: Andy Shevchenko R: Mika Westerberg +R: Jan Dabros L: linux-i2c@vger.kernel.org S: Maintained F: drivers/i2c/busses/i2c-designware-* diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index e7934ba79b02..ad245bbd965e 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -232,12 +232,13 @@ static const struct acpi_device_id acpi_apd_device_ids[] = { /* Generic apd devices */ #ifdef CONFIG_X86_AMD_PLATFORM_DEVICE { "AMD0010", APD_ADDR(cz_i2c_desc) }, - { "AMDI0010", APD_ADDR(wt_i2c_desc) }, { "AMD0020", APD_ADDR(cz_uart_desc) }, - { "AMDI0020", APD_ADDR(cz_uart_desc) }, - { "AMDI0022", APD_ADDR(cz_uart_desc) }, { "AMD0030", }, { "AMD0040", APD_ADDR(fch_misc_desc)}, + { "AMDI0010", APD_ADDR(wt_i2c_desc) }, + { "AMDI0019", APD_ADDR(wt_i2c_desc) }, + { "AMDI0020", APD_ADDR(cz_uart_desc) }, + { "AMDI0022", APD_ADDR(cz_uart_desc) }, { "HYGO0010", APD_ADDR(wt_i2c_desc) }, #endif #ifdef CONFIG_ARM64 diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 42da31c1ab70..5ea26a7aa1d5 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -553,6 +553,17 @@ config I2C_DESIGNWARE_PLATFORM This driver can also be built as a module. If so, the module will be called i2c-designware-platform. +config I2C_DESIGNWARE_AMDPSP + bool "AMD PSP I2C semaphore support" + depends on X86_MSR + depends on ACPI + depends on I2C_DESIGNWARE_PLATFORM + help + This driver enables managed host access to the selected I2C bus shared + between AMD CPU and AMD PSP. + + You should say Y if running on an AMD system equipped with the PSP. + config I2C_DESIGNWARE_BAYTRAIL bool "Intel Baytrail I2C semaphore support" depends on ACPI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 1d00dce77098..752f47be3fc1 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -54,6 +54,7 @@ i2c-designware-core-y += i2c-designware-master.o i2c-designware-core-$(CONFIG_I2C_DESIGNWARE_SLAVE) += i2c-designware-slave.o obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o i2c-designware-platform-y := i2c-designware-platdrv.o +i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_AMDPSP) += i2c-designware-amdpsp.o i2c-designware-platform-$(CONFIG_I2C_DESIGNWARE_BAYTRAIL) += i2c-designware-baytrail.o obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o i2c-designware-pci-y := i2c-designware-pcidrv.o diff --git a/drivers/i2c/busses/i2c-designware-amdpsp.c b/drivers/i2c/busses/i2c-designware-amdpsp.c new file mode 100644 index 000000000000..752e0024db03 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware-amdpsp.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include +#include + +#include + +#include "i2c-designware-core.h" + +#define MSR_AMD_PSP_ADDR 0xc00110a2 +#define PSP_MBOX_OFFSET 0x10570 +#define PSP_CMD_TIMEOUT_US (500 * USEC_PER_MSEC) + +#define PSP_I2C_REQ_BUS_CMD 0x64 +#define PSP_I2C_REQ_RETRY_CNT 10 +#define PSP_I2C_REQ_RETRY_DELAY_US (50 * USEC_PER_MSEC) +#define PSP_I2C_REQ_STS_OK 0x0 +#define PSP_I2C_REQ_STS_BUS_BUSY 0x1 +#define PSP_I2C_REQ_STS_INV_PARAM 0x3 + +#define PSP_MBOX_FIELDS_STS GENMASK(15, 0) +#define PSP_MBOX_FIELDS_CMD GENMASK(23, 16) +#define PSP_MBOX_FIELDS_RESERVED GENMASK(29, 24) +#define PSP_MBOX_FIELDS_RECOVERY BIT(30) +#define PSP_MBOX_FIELDS_READY BIT(31) + +struct psp_req_buffer_hdr { + u32 total_size; + u32 status; +}; + +enum psp_i2c_req_type { + PSP_I2C_REQ_ACQUIRE, + PSP_I2C_REQ_RELEASE, + PSP_I2C_REQ_MAX +}; + +struct psp_i2c_req { + struct psp_req_buffer_hdr hdr; + enum psp_i2c_req_type type; +}; + +struct psp_mbox { + u32 cmd_fields; + u64 i2c_req_addr; +} __packed; + +static DEFINE_MUTEX(psp_i2c_access_mutex); +static unsigned long psp_i2c_sem_acquired; +static void __iomem *mbox_iomem; +static u32 psp_i2c_access_count; +static bool psp_i2c_mbox_fail; +static struct device *psp_i2c_dev; + +/* + * Implementation of PSP-x86 i2c-arbitration mailbox introduced for AMD Cezanne + * family of SoCs. + */ + +static int psp_get_mbox_addr(unsigned long *mbox_addr) +{ + unsigned long long psp_mmio; + + if (rdmsrl_safe(MSR_AMD_PSP_ADDR, &psp_mmio)) + return -EIO; + + *mbox_addr = (unsigned long)(psp_mmio + PSP_MBOX_OFFSET); + + return 0; +} + +static int psp_mbox_probe(void) +{ + unsigned long mbox_addr; + int ret; + + ret = psp_get_mbox_addr(&mbox_addr); + if (ret) + return ret; + + mbox_iomem = ioremap(mbox_addr, sizeof(struct psp_mbox)); + if (!mbox_iomem) + return -ENOMEM; + + return 0; +} + +/* Recovery field should be equal 0 to start sending commands */ +static int psp_check_mbox_recovery(struct psp_mbox __iomem *mbox) +{ + u32 tmp; + + tmp = readl(&mbox->cmd_fields); + + return FIELD_GET(PSP_MBOX_FIELDS_RECOVERY, tmp); +} + +static int psp_wait_cmd(struct psp_mbox __iomem *mbox) +{ + u32 tmp, expected; + + /* Expect mbox_cmd to be cleared and ready bit to be set by PSP */ + expected = FIELD_PREP(PSP_MBOX_FIELDS_READY, 1); + + /* + * Check for readiness of PSP mailbox in a tight loop in order to + * process further as soon as command was consumed. + */ + return readl_poll_timeout(&mbox->cmd_fields, tmp, (tmp == expected), + 0, PSP_CMD_TIMEOUT_US); +} + +/* Status equal to 0 means that PSP succeed processing command */ +static u32 psp_check_mbox_sts(struct psp_mbox __iomem *mbox) +{ + u32 cmd_reg; + + cmd_reg = readl(&mbox->cmd_fields); + + return FIELD_GET(PSP_MBOX_FIELDS_STS, cmd_reg); +} + +static int psp_send_cmd(struct psp_i2c_req *req) +{ + struct psp_mbox __iomem *mbox = mbox_iomem; + phys_addr_t req_addr; + u32 cmd_reg; + + if (psp_check_mbox_recovery(mbox)) + return -EIO; + + if (psp_wait_cmd(mbox)) + return -EBUSY; + + /* + * Fill mailbox with address of command-response buffer, which will be + * used for sending i2c requests as well as reading status returned by + * PSP. Use physical address of buffer, since PSP will map this region. + */ + req_addr = __psp_pa((void *)req); + writeq(req_addr, &mbox->i2c_req_addr); + + /* Write command register to trigger processing */ + cmd_reg = FIELD_PREP(PSP_MBOX_FIELDS_CMD, PSP_I2C_REQ_BUS_CMD); + writel(cmd_reg, &mbox->cmd_fields); + + if (psp_wait_cmd(mbox)) + return -ETIMEDOUT; + + if (psp_check_mbox_sts(mbox)) + return -EIO; + + return 0; +} + +/* Helper to verify status returned by PSP */ +static int check_i2c_req_sts(struct psp_i2c_req *req) +{ + int status; + + status = readl(&req->hdr.status); + + switch (status) { + case PSP_I2C_REQ_STS_OK: + return 0; + case PSP_I2C_REQ_STS_BUS_BUSY: + return -EBUSY; + case PSP_I2C_REQ_STS_INV_PARAM: + default: + return -EIO; + }; +} + +static int psp_send_check_i2c_req(struct psp_i2c_req *req) +{ + /* + * Errors in x86-PSP i2c-arbitration protocol may occur at two levels: + * 1. mailbox communication - PSP is not operational or some IO errors + * with basic communication had happened; + * 2. i2c-requests - PSP refuses to grant i2c arbitration to x86 for too + * long. + * In order to distinguish between these two in error handling code, all + * errors on the first level (returned by psp_send_cmd) are shadowed by + * -EIO. + */ + if (psp_send_cmd(req)) + return -EIO; + + return check_i2c_req_sts(req); +} + +static int psp_send_i2c_req(enum psp_i2c_req_type i2c_req_type) +{ + struct psp_i2c_req *req; + unsigned long start; + int status, ret; + + /* Allocate command-response buffer */ + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->hdr.total_size = sizeof(*req); + req->type = i2c_req_type; + + start = jiffies; + ret = read_poll_timeout(psp_send_check_i2c_req, status, + (status != -EBUSY), + PSP_I2C_REQ_RETRY_DELAY_US, + PSP_I2C_REQ_RETRY_CNT * PSP_I2C_REQ_RETRY_DELAY_US, + 0, req); + if (ret) + goto cleanup; + + ret = status; + if (ret) + goto cleanup; + + dev_dbg(psp_i2c_dev, "Request accepted by PSP after %ums\n", + jiffies_to_msecs(jiffies - start)); + +cleanup: + kfree(req); + return ret; +} + +static int psp_acquire_i2c_bus(void) +{ + int status; + + mutex_lock(&psp_i2c_access_mutex); + + /* Return early if mailbox malfunctioned */ + if (psp_i2c_mbox_fail) + goto cleanup; + + /* + * Simply increment usage counter and return if PSP semaphore was + * already taken by kernel. + */ + if (psp_i2c_access_count) { + psp_i2c_access_count++; + goto cleanup; + }; + + status = psp_send_i2c_req(PSP_I2C_REQ_ACQUIRE); + if (status) { + if (status == -ETIMEDOUT) + dev_err(psp_i2c_dev, "Timed out waiting for PSP to release I2C bus\n"); + else + dev_err(psp_i2c_dev, "PSP communication error\n"); + + dev_err(psp_i2c_dev, "Assume i2c bus is for exclusive host usage\n"); + psp_i2c_mbox_fail = true; + goto cleanup; + } + + psp_i2c_sem_acquired = jiffies; + psp_i2c_access_count++; + + /* + * In case of errors with PSP arbitrator psp_i2c_mbox_fail variable is + * set above. As a consequence consecutive calls to acquire will bypass + * communication with PSP. At any case i2c bus is granted to the caller, + * thus always return success. + */ +cleanup: + mutex_unlock(&psp_i2c_access_mutex); + return 0; +} + +static void psp_release_i2c_bus(void) +{ + int status; + + mutex_lock(&psp_i2c_access_mutex); + + /* Return early if mailbox was malfunctional */ + if (psp_i2c_mbox_fail) + goto cleanup; + + /* + * If we are last owner of PSP semaphore, need to release aribtration + * via mailbox. + */ + psp_i2c_access_count--; + if (psp_i2c_access_count) + goto cleanup; + + /* Send a release command to PSP */ + status = psp_send_i2c_req(PSP_I2C_REQ_RELEASE); + if (status) { + if (status == -ETIMEDOUT) + dev_err(psp_i2c_dev, "Timed out waiting for PSP to acquire I2C bus\n"); + else + dev_err(psp_i2c_dev, "PSP communication error\n"); + + dev_err(psp_i2c_dev, "Assume i2c bus is for exclusive host usage\n"); + psp_i2c_mbox_fail = true; + goto cleanup; + } + + dev_dbg(psp_i2c_dev, "PSP semaphore held for %ums\n", + jiffies_to_msecs(jiffies - psp_i2c_sem_acquired)); + +cleanup: + mutex_unlock(&psp_i2c_access_mutex); +} + +/* + * Locking methods are based on the default implementation from + * drivers/i2c/i2c-core-base.c, but with psp acquire and release operations + * added. With this in place we can ensure that i2c clients on the bus shared + * with psp are able to lock HW access to the bus for arbitrary number of + * operations - that is e.g. write-wait-read. + */ +static void i2c_adapter_dw_psp_lock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + psp_acquire_i2c_bus(); + rt_mutex_lock_nested(&adapter->bus_lock, i2c_adapter_depth(adapter)); +} + +static int i2c_adapter_dw_psp_trylock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + int ret; + + ret = rt_mutex_trylock(&adapter->bus_lock); + if (ret) + return ret; + + psp_acquire_i2c_bus(); + + return ret; +} + +static void i2c_adapter_dw_psp_unlock_bus(struct i2c_adapter *adapter, + unsigned int flags) +{ + psp_release_i2c_bus(); + rt_mutex_unlock(&adapter->bus_lock); +} + +static const struct i2c_lock_operations i2c_dw_psp_lock_ops = { + .lock_bus = i2c_adapter_dw_psp_lock_bus, + .trylock_bus = i2c_adapter_dw_psp_trylock_bus, + .unlock_bus = i2c_adapter_dw_psp_unlock_bus, +}; + +int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev) +{ + int ret; + + if (!dev) + return -ENODEV; + + if (!(dev->flags & ARBITRATION_SEMAPHORE)) + return -ENODEV; + + /* Allow to bind only one instance of a driver */ + if (psp_i2c_dev) + return -EEXIST; + + psp_i2c_dev = dev->dev; + + ret = psp_mbox_probe(); + if (ret) + return ret; + + dev_info(psp_i2c_dev, "I2C bus managed by AMD PSP\n"); + + /* + * Install global locking callbacks for adapter as well as internal i2c + * controller locks. + */ + dev->adapter.lock_ops = &i2c_dw_psp_lock_ops; + dev->acquire_lock = psp_acquire_i2c_bus; + dev->release_lock = psp_release_i2c_bus; + + return 0; +} + +/* Unmap area used as a mailbox with PSP */ +void i2c_dw_amdpsp_remove_lock_support(struct dw_i2c_dev *dev) +{ + iounmap(mbox_iomem); +} diff --git a/drivers/i2c/busses/i2c-designware-baytrail.c b/drivers/i2c/busses/i2c-designware-baytrail.c index c6a7a00e1d52..45774aa47c28 100644 --- a/drivers/i2c/busses/i2c-designware-baytrail.c +++ b/drivers/i2c/busses/i2c-designware-baytrail.c @@ -12,25 +12,25 @@ #include "i2c-designware-core.h" -int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) +int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev) { acpi_status status; unsigned long long shared_host = 0; acpi_handle handle; - if (!dev || !dev->dev) - return 0; + if (!dev) + return -ENODEV; handle = ACPI_HANDLE(dev->dev); if (!handle) - return 0; + return -ENODEV; status = acpi_evaluate_integer(handle, "_SEM", NULL, &shared_host); if (ACPI_FAILURE(status)) - return 0; + return -ENODEV; if (!shared_host) - return 0; + return -ENODEV; if (!iosf_mbi_available()) return -EPROBE_DEFER; diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 4b26cba40139..1d65212fddbd 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -227,6 +227,8 @@ struct reset_control; * @hs_lcnt: high speed LCNT value * @acquire_lock: function to acquire a hardware lock on the bus * @release_lock: function to release a hardware lock on the bus + * @semaphore_idx: Index of table with semaphore type attached to the bus. It's + * -1 if there is no semaphore. * @shared_with_punit: true if this bus is shared with the SoCs PUNIT * @disable: function to disable the controller * @disable_int: function to disable all interrupts @@ -285,6 +287,7 @@ struct dw_i2c_dev { u16 hs_lcnt; int (*acquire_lock)(void); void (*release_lock)(void); + int semaphore_idx; bool shared_with_punit; void (*disable)(struct dw_i2c_dev *dev); void (*disable_int)(struct dw_i2c_dev *dev); @@ -297,6 +300,7 @@ struct dw_i2c_dev { #define ACCESS_INTR_MASK BIT(0) #define ACCESS_NO_IRQ_SUSPEND BIT(1) +#define ARBITRATION_SEMAPHORE BIT(2) #define MODEL_MSCC_OCELOT BIT(8) #define MODEL_BAIKAL_BT1 BIT(9) @@ -310,6 +314,11 @@ struct dw_i2c_dev { #define AMD_UCSI_INTR_REG 0x474 #define AMD_UCSI_INTR_EN 0xd +struct i2c_dw_semaphore_callbacks { + int (*probe)(struct dw_i2c_dev *dev); + void (*remove)(struct dw_i2c_dev *dev); +}; + int i2c_dw_init_regmap(struct dw_i2c_dev *dev); u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset); u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset); @@ -370,9 +379,12 @@ static inline void i2c_dw_configure(struct dw_i2c_dev *dev) } #if IS_ENABLED(CONFIG_I2C_DESIGNWARE_BAYTRAIL) -extern int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev); -#else -static inline int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) { return 0; } +int i2c_dw_baytrail_probe_lock_support(struct dw_i2c_dev *dev); +#endif + +#if IS_ENABLED(CONFIG_I2C_DESIGNWARE_AMDPSP) +int i2c_dw_amdpsp_probe_lock_support(struct dw_i2c_dev *dev); +void i2c_dw_amdpsp_remove_lock_support(struct dw_i2c_dev *dev); #endif int i2c_dw_validate_speed(struct dw_i2c_dev *dev); diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 2bd81abc86f6..9973ac894a51 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -50,6 +50,7 @@ static const struct acpi_device_id dw_i2c_acpi_match[] = { { "808622C1", ACCESS_NO_IRQ_SUSPEND }, { "AMD0010", ACCESS_INTR_MASK }, { "AMDI0010", ACCESS_INTR_MASK }, + { "AMDI0019", ACCESS_INTR_MASK | ARBITRATION_SEMAPHORE }, { "AMDI0510", 0 }, { "APMC0D0F", 0 }, { "HISI02A1", 0 }, @@ -204,6 +205,63 @@ static const struct dmi_system_id dw_i2c_hwmon_class_dmi[] = { { } /* terminate list */ }; +static const struct i2c_dw_semaphore_callbacks i2c_dw_semaphore_cb_table[] = { +#ifdef CONFIG_I2C_DESIGNWARE_BAYTRAIL + { + .probe = i2c_dw_baytrail_probe_lock_support, + }, +#endif +#ifdef CONFIG_I2C_DESIGNWARE_AMDPSP + { + .probe = i2c_dw_amdpsp_probe_lock_support, + .remove = i2c_dw_amdpsp_remove_lock_support, + }, +#endif + {} +}; + +static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) +{ + const struct i2c_dw_semaphore_callbacks *ptr; + int i = 0; + int ret; + + ptr = i2c_dw_semaphore_cb_table; + + dev->semaphore_idx = -1; + + while (ptr->probe) { + ret = ptr->probe(dev); + if (ret) { + /* + * If there is no semaphore device attached to this + * controller, we shouldn't abort general i2c_controller + * probe. + */ + if (ret != -ENODEV) + return ret; + + i++; + ptr++; + continue; + } + + dev->semaphore_idx = i; + break; + } + + return 0; +} + +static void i2c_dw_remove_lock_support(struct dw_i2c_dev *dev) +{ + if (dev->semaphore_idx < 0) + return; + + if (i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove) + i2c_dw_semaphore_cb_table[dev->semaphore_idx].remove(dev); +} + static int dw_i2c_plat_probe(struct platform_device *pdev) { struct i2c_adapter *adap; @@ -334,6 +392,8 @@ static int dw_i2c_plat_remove(struct platform_device *pdev) pm_runtime_put_sync(&pdev->dev); dw_i2c_plat_pm_cleanup(dev); + i2c_dw_remove_lock_support(dev); + reset_control_assert(dev->rst); return 0; -- cgit v1.2.3 From ea01b71b07993d5c518496692f476a3c6b5d9786 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 3 Feb 2022 15:33:17 +0100 Subject: i2c: rcar: Add R-Car Gen4 support Add support for the I2C Bus Interface on R-Car Gen4 SoCs (e.g. R-Car S4-8) by matching on a family-specific compatible value. While I2C on R-Car Gen4 does support some extra features (Slave Clock Stretch Select), for now it is treated the same as I2C on R-Car Gen3. Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang [wsa: removed incorrect "FM+" from commit message] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-rcar.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index f71c730f9838..0db3d7559066 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -1008,6 +1008,7 @@ static const struct of_device_id rcar_i2c_dt_ids[] = { { .compatible = "renesas,rcar-gen1-i2c", .data = (void *)I2C_RCAR_GEN1 }, { .compatible = "renesas,rcar-gen2-i2c", .data = (void *)I2C_RCAR_GEN2 }, { .compatible = "renesas,rcar-gen3-i2c", .data = (void *)I2C_RCAR_GEN3 }, + { .compatible = "renesas,rcar-gen4-i2c", .data = (void *)I2C_RCAR_GEN3 }, {}, }; MODULE_DEVICE_TABLE(of, rcar_i2c_dt_ids); -- cgit v1.2.3 From d0aee048d648ec2d9aa7af43b127ebf847d497d5 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Fri, 11 Feb 2022 11:58:06 +0100 Subject: i2c: brcmstb: allow compiling on BCM4908 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BCM4908 SoCs use the same I2C hardware block as STB and BCM63xx devices. Signed-off-by: Rafał Miłecki Acked-by: Florian Fainelli Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 5ea26a7aa1d5..1fbb5f1fa7b1 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -477,8 +477,8 @@ config I2C_BCM_KONA config I2C_BRCMSTB tristate "BRCM Settop/DSL I2C controller" - depends on ARCH_BCM2835 || ARCH_BRCMSTB || BMIPS_GENERIC || \ - ARCH_BCM_63XX || COMPILE_TEST + depends on ARCH_BCM2835 || ARCH_BCM4908 || ARCH_BCM_63XX || \ + ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST default y help If you say yes to this option, support will be included for the -- cgit v1.2.3 From 481153991c410e322383490527c20b0c41dbf652 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 10 Feb 2022 22:33:41 +0100 Subject: i2c: don't expose function which is only used internally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit i2c_setup_smbus_alert() is only needed within the I2C core, so no need to expose it to other modules. Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-smbus.c | 1 - drivers/i2c/i2c-core.h | 9 +++++++++ include/linux/i2c-smbus.h | 8 -------- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 304c2c8fee68..053b215308c4 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -716,5 +716,4 @@ int i2c_setup_smbus_alert(struct i2c_adapter *adapter) return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL)); } -EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert); #endif diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h index 8ce261167a2d..87e2c914f1c5 100644 --- a/drivers/i2c/i2c-core.h +++ b/drivers/i2c/i2c-core.h @@ -86,3 +86,12 @@ void of_i2c_register_devices(struct i2c_adapter *adap); static inline void of_i2c_register_devices(struct i2c_adapter *adap) { } #endif extern struct notifier_block i2c_of_notifier; + +#if IS_ENABLED(CONFIG_I2C_SMBUS) +int i2c_setup_smbus_alert(struct i2c_adapter *adap); +#else +static inline int i2c_setup_smbus_alert(struct i2c_adapter *adap) +{ + return 0; +} +#endif diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h index 95cf902e0bda..ced1c6ead52a 100644 --- a/include/linux/i2c-smbus.h +++ b/include/linux/i2c-smbus.h @@ -30,14 +30,6 @@ struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter, struct i2c_smbus_alert_setup *setup); int i2c_handle_smbus_alert(struct i2c_client *ara); -#if IS_ENABLED(CONFIG_I2C_SMBUS) -int i2c_setup_smbus_alert(struct i2c_adapter *adap); -#else -static inline int i2c_setup_smbus_alert(struct i2c_adapter *adap) -{ - return 0; -} -#endif #if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_I2C_SLAVE) struct i2c_client *i2c_new_slave_host_notify_device(struct i2c_adapter *adapter); void i2c_free_slave_host_notify_device(struct i2c_client *client); -- cgit v1.2.3 From 9c02d4011e92b92aa008d6907216a8e501b0443a Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Fri, 11 Feb 2022 17:00:01 +0200 Subject: i2c: i801: Add support for Intel Raptor Lake PCH-S Add SMBus PCI ID on Intel Raptor Lake PCH-S. Signed-off-by: Jarkko Nikula Reviewed-by: Jean Delvare Signed-off-by: Wolfram Sang --- Documentation/i2c/busses/i2c-i801.rst | 1 + drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 3 +++ 3 files changed, 5 insertions(+) (limited to 'drivers/i2c') diff --git a/Documentation/i2c/busses/i2c-i801.rst b/Documentation/i2c/busses/i2c-i801.rst index 42bbdd6e7fd8..cad59170b2ad 100644 --- a/Documentation/i2c/busses/i2c-i801.rst +++ b/Documentation/i2c/busses/i2c-i801.rst @@ -45,6 +45,7 @@ Supported adapters: * Intel Jasper Lake (SOC) * Intel Emmitsburg (PCH) * Intel Alder Lake (PCH) + * Intel Raptor Lake (PCH) Datasheets: Publicly available at the Intel website diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 1fbb5f1fa7b1..bfe17c42e11d 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -148,6 +148,7 @@ config I2C_I801 Jasper Lake (SOC) Emmitsburg (PCH) Alder Lake (PCH) + Raptor Lake (PCH) This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 7428cc6af5cc..36b086ef1378 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -75,6 +75,7 @@ * Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes * Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes * Alder Lake-M (PCH) 0x54a3 32 hard yes yes yes + * Raptor Lake-S (PCH) 0x7a23 32 hard yes yes yes * * Features supported by this driver: * Software PEC no @@ -228,6 +229,7 @@ #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_P_SMBUS 0x51a3 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_M_SMBUS 0x54a3 #define PCI_DEVICE_ID_INTEL_BROXTON_SMBUS 0x5ad4 +#define PCI_DEVICE_ID_INTEL_RAPTOR_LAKE_S_SMBUS 0x7a23 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_S_SMBUS 0x7aa3 #define PCI_DEVICE_ID_INTEL_LYNXPOINT_SMBUS 0x8c22 #define PCI_DEVICE_ID_INTEL_WILDCATPOINT_SMBUS 0x8ca2 @@ -1041,6 +1043,7 @@ static const struct pci_device_id i801_ids[] = { { PCI_DEVICE_DATA(INTEL, ALDER_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, { PCI_DEVICE_DATA(INTEL, ALDER_LAKE_P_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, { PCI_DEVICE_DATA(INTEL, ALDER_LAKE_M_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, + { PCI_DEVICE_DATA(INTEL, RAPTOR_LAKE_S_SMBUS, FEATURES_ICH5 | FEATURE_TCO_CNL) }, { 0, } }; -- cgit v1.2.3 From 4ebf4987c0918ec6a08ece8ee745af44af02dee0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Jan 2022 16:19:31 +0200 Subject: i2c: Introduce common module to instantiate CCGx UCSI Introduce a common module to provide an API to instantiate UCSI device for Cypress CCGx Type-C controller. Individual bus drivers need to select this one on demand. Signed-off-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 7 +++++++ drivers/i2c/busses/Makefile | 3 +++ drivers/i2c/busses/i2c-ccgx-ucsi.c | 30 ++++++++++++++++++++++++++++++ drivers/i2c/busses/i2c-ccgx-ucsi.h | 11 +++++++++++ 4 files changed, 51 insertions(+) create mode 100644 drivers/i2c/busses/i2c-ccgx-ucsi.c create mode 100644 drivers/i2c/busses/i2c-ccgx-ucsi.h (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index bfe17c42e11d..6df1bd150233 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -9,6 +9,13 @@ menu "I2C Hardware Bus support" comment "PC SMBus host controller drivers" depends on PCI +config I2C_CCGX_UCSI + tristate + help + A common module to provide an API to instantiate UCSI device + for Cypress CCGx Type-C controller. Individual bus drivers + need to select this one on demand. + config I2C_ALI1535 tristate "ALI 1535" depends on PCI diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 752f47be3fc1..479f60e4ee3d 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -6,6 +6,9 @@ # ACPI drivers obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o +# Auxiliary I2C/SMBus modules +obj-$(CONFIG_I2C_CCGX_UCSI) += i2c-ccgx-ucsi.o + # PC SMBus host controller drivers obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o diff --git a/drivers/i2c/busses/i2c-ccgx-ucsi.c b/drivers/i2c/busses/i2c-ccgx-ucsi.c new file mode 100644 index 000000000000..092dc92dea9f --- /dev/null +++ b/drivers/i2c/busses/i2c-ccgx-ucsi.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Instantiate UCSI device for Cypress CCGx Type-C controller. + * Derived from i2c-designware-pcidrv.c and i2c-nvidia-gpu.c. + */ + +#include +#include +#include +#include + +#include "i2c-ccgx-ucsi.h" + +struct software_node; + +struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq, + const struct software_node *swnode) +{ + struct i2c_board_info info = {}; + + strscpy(info.type, "ccgx-ucsi", sizeof(info.type)); + info.addr = 0x08; + info.irq = irq; + info.swnode = swnode; + + return i2c_new_client_device(adapter, &info); +} +EXPORT_SYMBOL_GPL(i2c_new_ccgx_ucsi); + +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-ccgx-ucsi.h b/drivers/i2c/busses/i2c-ccgx-ucsi.h new file mode 100644 index 000000000000..739ac7a4b117 --- /dev/null +++ b/drivers/i2c/busses/i2c-ccgx-ucsi.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __I2C_CCGX_UCSI_H_ +#define __I2C_CCGX_UCSI_H_ + +struct i2c_adapter; +struct i2c_client; +struct software_node; + +struct i2c_client *i2c_new_ccgx_ucsi(struct i2c_adapter *adapter, int irq, + const struct software_node *swnode); +#endif /* __I2C_CCGX_UCSI_H_ */ -- cgit v1.2.3 From 2079563d6f60e63f7fc5d1b0b7a48dbb93c209d1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Jan 2022 16:19:32 +0200 Subject: i2c: nvidia-gpu: Switch to use i2c_new_ccgx_ucsi() Instead of open coded variant switch to use i2c_new_ccgx_ucsi(). Signed-off-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-nvidia-gpu.c | 26 ++++++-------------------- 2 files changed, 7 insertions(+), 20 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 6df1bd150233..df3ecbe8ff25 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -253,6 +253,7 @@ config I2C_NFORCE2_S4985 config I2C_NVIDIA_GPU tristate "NVIDIA GPU I2C controller" depends on PCI + select I2C_CCGX_UCSI 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 diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index b5055a3cbd93..8117c3674209 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -17,6 +17,8 @@ #include +#include "i2c-ccgx-ucsi.h" + /* I2C definitions */ #define I2C_MST_CNTL 0x00 #define I2C_MST_CNTL_GEN_START BIT(0) @@ -266,23 +268,6 @@ static const struct software_node ccgx_node = { .properties = ccgx_props, }; -static int gpu_populate_client(struct gpu_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->gpu_ccgx_ucsi->swnode = &ccgx_node; - i2cd->ccgx_client = i2c_new_client_device(&i2cd->adapter, i2cd->gpu_ccgx_ucsi); - return PTR_ERR_OR_ZERO(i2cd->ccgx_client); -} - static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct gpu_i2c_dev *i2cd; @@ -328,9 +313,10 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (status < 0) goto free_irq_vectors; - status = gpu_populate_client(i2cd, pdev->irq); - if (status < 0) { - dev_err(&pdev->dev, "gpu_populate_client failed %d\n", status); + i2cd->ccgx_client = i2c_new_ccgx_ucsi(&i2cd->adapter, pdev->irq, &ccgx_node); + if (IS_ERR(i2cd->ccgx_client)) { + status = dev_err_probe(&pdev->dev, PTR_ERR(i2cd->ccgx_client), + "register UCSI failed\n"); goto del_adapter; } -- cgit v1.2.3 From c2c25be6dc94132bca85f5b2c6ee9f6d7ab0173f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Jan 2022 16:19:33 +0200 Subject: i2c: nvidia-gpu: Use temporary variable for struct device Use temporary variable for struct device to make code neater. Signed-off-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-nvidia-gpu.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index 8117c3674209..a82be377146e 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -270,19 +270,20 @@ static const struct software_node ccgx_node = { static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + struct device *dev = &pdev->dev; struct gpu_i2c_dev *i2cd; int status; - i2cd = devm_kzalloc(&pdev->dev, sizeof(*i2cd), GFP_KERNEL); + i2cd = devm_kzalloc(dev, sizeof(*i2cd), GFP_KERNEL); if (!i2cd) return -ENOMEM; - i2cd->dev = &pdev->dev; - dev_set_drvdata(&pdev->dev, i2cd); + i2cd->dev = dev; + dev_set_drvdata(dev, i2cd); status = pcim_enable_device(pdev); if (status < 0) { - dev_err(&pdev->dev, "pcim_enable_device failed %d\n", status); + dev_err(dev, "pcim_enable_device failed %d\n", status); return status; } @@ -290,13 +291,13 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) i2cd->regs = pcim_iomap(pdev, 0, 0); if (!i2cd->regs) { - dev_err(&pdev->dev, "pcim_iomap failed\n"); + dev_err(dev, "pcim_iomap failed\n"); return -ENOMEM; } status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); if (status < 0) { - dev_err(&pdev->dev, "pci_alloc_irq_vectors err %d\n", status); + dev_err(dev, "pci_alloc_irq_vectors err %d\n", status); return status; } @@ -308,22 +309,21 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) sizeof(i2cd->adapter.name)); i2cd->adapter.algo = &gpu_i2c_algorithm; i2cd->adapter.quirks = &gpu_i2c_quirks; - i2cd->adapter.dev.parent = &pdev->dev; + i2cd->adapter.dev.parent = dev; status = i2c_add_adapter(&i2cd->adapter); if (status < 0) goto free_irq_vectors; i2cd->ccgx_client = i2c_new_ccgx_ucsi(&i2cd->adapter, pdev->irq, &ccgx_node); if (IS_ERR(i2cd->ccgx_client)) { - status = dev_err_probe(&pdev->dev, PTR_ERR(i2cd->ccgx_client), - "register UCSI failed\n"); + status = dev_err_probe(dev, PTR_ERR(i2cd->ccgx_client), "register UCSI failed\n"); 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); + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put_autosuspend(dev); + pm_runtime_allow(dev); return 0; @@ -336,7 +336,7 @@ free_irq_vectors: static void gpu_i2c_remove(struct pci_dev *pdev) { - struct gpu_i2c_dev *i2cd = dev_get_drvdata(&pdev->dev); + struct gpu_i2c_dev *i2cd = pci_get_drvdata(pdev); pm_runtime_get_noresume(i2cd->dev); i2c_del_adapter(&i2cd->adapter); -- cgit v1.2.3 From c74a30ce137636d1a12abde357718e45524c2b71 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Jan 2022 16:19:34 +0200 Subject: i2c: nvidia-gpu: Convert to use dev_err_probe() It's fine to call dev_err_probe() in ->probe() when error code is known. Convert the driver to use dev_err_probe(). Signed-off-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-nvidia-gpu.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index a82be377146e..6920c1b9a126 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -282,24 +282,18 @@ static int gpu_i2c_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev_set_drvdata(dev, i2cd); status = pcim_enable_device(pdev); - if (status < 0) { - dev_err(dev, "pcim_enable_device failed %d\n", status); - return status; - } + if (status < 0) + return dev_err_probe(dev, status, "pcim_enable_device failed\n"); pci_set_master(pdev); i2cd->regs = pcim_iomap(pdev, 0, 0); - if (!i2cd->regs) { - dev_err(dev, "pcim_iomap failed\n"); - return -ENOMEM; - } + if (!i2cd->regs) + return dev_err_probe(dev, -ENOMEM, "pcim_iomap failed\n"); status = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); - if (status < 0) { - dev_err(dev, "pci_alloc_irq_vectors err %d\n", status); - return status; - } + if (status < 0) + return dev_err_probe(dev, status, "pci_alloc_irq_vectors err\n"); gpu_enable_i2c_bus(i2cd); -- cgit v1.2.3 From 531310dd5d9f78b5d9e28e742d55c775f114ad5a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Jan 2022 16:19:35 +0200 Subject: i2c: designware-pci: Switch to use i2c_new_ccgx_ucsi() Instead of open coded variant switch to use i2c_new_ccgx_ucsi(). Signed-off-by: Andy Shevchenko Acked-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-designware-pcidrv.c | 30 +++++------------------------- 2 files changed, 6 insertions(+), 25 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index df3ecbe8ff25..0e6070bcf241 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -590,6 +590,7 @@ config I2C_DESIGNWARE_PCI tristate "Synopsys DesignWare PCI" depends on PCI select I2C_DESIGNWARE_CORE + select I2C_CCGX_UCSI help If you say yes to this option, support will be included for the Synopsys DesignWare I2C adapter. Only master mode is supported. diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index ef4250f8852b..2782dddfb087 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -24,6 +24,7 @@ #include #include "i2c-designware-core.h" +#include "i2c-ccgx-ucsi.h" #define DRIVER_NAME "i2c-designware-pci" #define AMD_CLK_RATE_HZ 100000 @@ -125,26 +126,6 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c) return -ENODEV; } - /* - * TODO find a better way how to deduplicate instantiation - * of USB PD slave device from nVidia GPU driver. - */ -static int navi_amd_register_client(struct dw_i2c_dev *dev) -{ - struct i2c_board_info info; - - memset(&info, 0, sizeof(struct i2c_board_info)); - strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE); - info.addr = 0x08; - info.irq = dev->irq; - - dev->slave = i2c_new_client_device(&dev->adapter, &info); - if (IS_ERR(dev->slave)) - return PTR_ERR(dev->slave); - - return 0; -} - static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c) { struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev); @@ -325,11 +306,10 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, } if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) { - r = navi_amd_register_client(dev); - if (r) { - dev_err(dev->dev, "register client failed with %d\n", r); - return r; - } + dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, NULL); + if (IS_ERR(dev->slave)) + return dev_err_probe(dev->dev, PTR_ERR(dev->slave), + "register UCSI failed\n"); } pm_runtime_set_autosuspend_delay(&pdev->dev, 1000); -- cgit v1.2.3 From 1e4fe5430bd791fb2a2d7b41ac25d1aaa5d0726a Mon Sep 17 00:00:00 2001 From: Yang Li Date: Mon, 14 Feb 2022 20:47:01 +0800 Subject: i2c: designware: remove unneeded semicolon Eliminate the following coccicheck warnings: ./drivers/i2c/busses/i2c-designware-amdpsp.c:172:2-3: Unneeded semicolon ./drivers/i2c/busses/i2c-designware-amdpsp.c:245:2-3: Unneeded semicolon Reported-by: Abaci Robot Signed-off-by: Yang Li Acked-by: Jarkko Nikula Reviewed-by: Jan Dabros Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-amdpsp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-amdpsp.c b/drivers/i2c/busses/i2c-designware-amdpsp.c index 752e0024db03..9d37becbd846 100644 --- a/drivers/i2c/busses/i2c-designware-amdpsp.c +++ b/drivers/i2c/busses/i2c-designware-amdpsp.c @@ -172,7 +172,7 @@ static int check_i2c_req_sts(struct psp_i2c_req *req) case PSP_I2C_REQ_STS_INV_PARAM: default: return -EIO; - }; + } } static int psp_send_check_i2c_req(struct psp_i2c_req *req) @@ -245,7 +245,7 @@ static int psp_acquire_i2c_bus(void) if (psp_i2c_access_count) { psp_i2c_access_count++; goto cleanup; - }; + } status = psp_send_i2c_req(PSP_I2C_REQ_ACQUIRE); if (status) { -- cgit v1.2.3 From 19e138e43a0820bb4dbf8fb5c7691f82e9221f2b Mon Sep 17 00:00:00 2001 From: Martin Povišer Date: Fri, 4 Feb 2022 10:59:14 +0100 Subject: i2c: pasemi: Drop I2C classes from platform driver variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop I2C device-probing classes from platform variant of the PASemi controller as it is only used on platforms where I2C devices should be instantiated in devicetree. (The I2C_CLASS_DEPRECATED flag is not raised as up to this point no devices relied on the old behavior.) Fixes: d88ae2932df0 ("i2c: pasemi: Add Apple platform driver") Signed-off-by: Martin Povišer Reviewed-by: Sven Peter Acked-by: Hector Martin Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-pasemi-core.c | 1 - drivers/i2c/busses/i2c-pasemi-pci.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-pasemi-core.c b/drivers/i2c/busses/i2c-pasemi-core.c index 4e161a4089d8..7728c8460dc0 100644 --- a/drivers/i2c/busses/i2c-pasemi-core.c +++ b/drivers/i2c/busses/i2c-pasemi-core.c @@ -333,7 +333,6 @@ int pasemi_i2c_common_probe(struct pasemi_smbus *smbus) smbus->adapter.owner = THIS_MODULE; snprintf(smbus->adapter.name, sizeof(smbus->adapter.name), "PA Semi SMBus adapter (%s)", dev_name(smbus->dev)); - smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; diff --git a/drivers/i2c/busses/i2c-pasemi-pci.c b/drivers/i2c/busses/i2c-pasemi-pci.c index 1ab1f28744fb..cfc89e04eb94 100644 --- a/drivers/i2c/busses/i2c-pasemi-pci.c +++ b/drivers/i2c/busses/i2c-pasemi-pci.c @@ -56,6 +56,7 @@ static int pasemi_smb_pci_probe(struct pci_dev *dev, if (!smbus->ioaddr) return -EBUSY; + smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; error = pasemi_i2c_common_probe(smbus); if (error) return error; -- cgit v1.2.3 From 017b32e6da8d4a9673bf68ab4ac1260ef74e9695 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 3 Feb 2022 18:47:13 +0200 Subject: i2c: qcom-cci: add sm8450 compatible Add QCOM SM8450 specific compatible for CCI controller, which is equal to CCI controllers found on QCOM SDM845 and QCOM SM8250 SoCs. Signed-off-by: Vladimir Zapolskiy Reviewed-by: Robert Foss Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qcom-cci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index c1de8eb66169..649c91993744 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2012-2016, The Linux Foundation. All rights reserved. -// Copyright (c) 2017-20 Linaro Limited. +// Copyright (c) 2017-2022 Linaro Limited. #include #include @@ -770,6 +770,7 @@ static const struct of_device_id cci_dt_match[] = { { .compatible = "qcom,msm8996-cci", .data = &cci_v2_data}, { .compatible = "qcom,sdm845-cci", .data = &cci_v2_data}, { .compatible = "qcom,sm8250-cci", .data = &cci_v2_data}, + { .compatible = "qcom,sm8450-cci", .data = &cci_v2_data}, {} }; MODULE_DEVICE_TABLE(of, cci_dt_match); -- cgit v1.2.3 From 8302532f47bb6c3aa1ed2043d30187ca307f176a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 4 Feb 2022 17:59:20 +0200 Subject: i2c: smbus: Check for parent device before dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An I²C adapter might be instantiated without parent. In such case there is no property can be retrieved. Skip SMBus alert setup when this happens. Fixes: a263a84088f6 ("i2c: smbus: Use device_*() functions instead of of_*()") Reported-by: syzbot+0591ccf54ee05344e4eb@syzkaller.appspotmail.com Signed-off-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-smbus.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 053b215308c4..e3b96fc53b5c 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -705,10 +705,14 @@ EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device); #if IS_ENABLED(CONFIG_I2C_SMBUS) int i2c_setup_smbus_alert(struct i2c_adapter *adapter) { + struct device *parent = adapter->dev.parent; int irq; - irq = device_property_match_string(adapter->dev.parent, "interrupt-names", - "smbus_alert"); + /* Adapter instantiated without parent, skip the SMBus alert setup */ + if (!parent) + return 0; + + irq = device_property_match_string(parent, "interrupt-names", "smbus_alert"); if (irq == -EINVAL || irq == -ENODATA) return 0; else if (irq < 0) -- cgit v1.2.3 From 68ea1b2c3386ad484b7daa4a64fa7b3be661607f Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 11 Feb 2022 19:14:55 +0100 Subject: i2c: core: Use generic_handle_irq_safe() in i2c_handle_smbus_host_notify(). The i2c-i801 driver invokes i2c_handle_smbus_host_notify() from his interrupt service routine. On PREEMPT_RT i2c-i801's handler is forced threaded with enabled interrupts which leads to a warning by handle_irq_event_percpu() assuming that irq_default_primary_handler() enabled interrupts. i2c-i801's interrupt handler can't be made non-threaded because the interrupt line is shared with other devices. Use generic_handle_irq_safe() which can invoked with disabled and enabled interrupts. Reported-by: Michael Below Link: https://bugs.debian.org/1002537 Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Oleksandr Natalenko Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 32a45260c1f3..d43db2c3876e 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1424,7 +1424,7 @@ int i2c_handle_smbus_host_notify(struct i2c_adapter *adap, unsigned short addr) if (irq <= 0) return -ENXIO; - generic_handle_irq(irq); + generic_handle_irq_safe(irq); return 0; } -- cgit v1.2.3 From a5ea32579f08b9f612e3091467d01b6503ba24dd Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Fri, 11 Feb 2022 19:14:56 +0100 Subject: i2c: cht-wc: Use generic_handle_irq_safe(). Instead of manually disabling interrupts before invoking use generic_handle_irq_safe() which can be invoked with enabled and disabled interrupts. Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Hans de Goede Acked-by: Wolfram Sang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-cht-wc.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-cht-wc.c b/drivers/i2c/busses/i2c-cht-wc.c index 1cf68f85b2e1..8ccf0c928bb4 100644 --- a/drivers/i2c/busses/i2c-cht-wc.c +++ b/drivers/i2c/busses/i2c-cht-wc.c @@ -99,15 +99,8 @@ static irqreturn_t cht_wc_i2c_adap_thread_handler(int id, void *data) * interrupt handler as well, so running the client irq handler from * this thread will cause things to lock up. */ - if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) { - /* - * generic_handle_irq expects local IRQs to be disabled - * as normally it is called from interrupt context. - */ - local_irq_disable(); - generic_handle_irq(adap->client_irq); - local_irq_enable(); - } + if (reg & CHT_WC_EXTCHGRIRQ_CLIENT_IRQ) + generic_handle_irq_safe(adap->client_irq); return IRQ_HANDLED; } -- cgit v1.2.3 From da2e86c0bd7bc6fdb116dd03c041cf816205cbdc Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 9 Feb 2022 23:22:32 +0000 Subject: i2c: riic: Simplify reset handling Read reset phandle as optional instead of exclusive so that all the DT's passing the reset phandle can be used to assert/deassert the reset line. With this change we don't have to differentiate the RZ/G2L SoC. With the above changes we no longer need the "renesas,riic-r9a07g044" compatible string, so drop it from riic_i2c_dt_ids[]. No changes are required to the r9a07g044.dtsi as we already have "renesas,riic-rz" as a fallback compatible string. While at it, check the return code of reset_control_deassert() as it might fail and also add a devres action to assert the reset line. Signed-off-by: Lad Prabhakar Reviewed-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-riic.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index 8dfd27dc6149..cded77e06670 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -88,11 +88,6 @@ #define RIIC_INIT_MSG -1 -enum riic_type { - RIIC_RZ_A, - RIIC_RZ_G2L, -}; - struct riic_dev { void __iomem *base; u8 *buf; @@ -396,6 +391,11 @@ static struct riic_irq_desc riic_irqs[] = { { .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" }, }; +static void riic_reset_control_assert(void *data) +{ + reset_control_assert(data); +} + static int riic_i2c_probe(struct platform_device *pdev) { struct riic_dev *riic; @@ -404,7 +404,6 @@ static int riic_i2c_probe(struct platform_device *pdev) struct i2c_timings i2c_t; struct reset_control *rstc; int i, ret; - enum riic_type type; riic = devm_kzalloc(&pdev->dev, sizeof(*riic), GFP_KERNEL); if (!riic) @@ -421,16 +420,18 @@ static int riic_i2c_probe(struct platform_device *pdev) return PTR_ERR(riic->clk); } - type = (enum riic_type)of_device_get_match_data(&pdev->dev); - if (type == RIIC_RZ_G2L) { - rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(rstc)) { - dev_err(&pdev->dev, "Error: missing reset ctrl\n"); - return PTR_ERR(rstc); - } + rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(rstc)) + return dev_err_probe(&pdev->dev, PTR_ERR(rstc), + "Error: missing reset ctrl\n"); - reset_control_deassert(rstc); - } + ret = reset_control_deassert(rstc); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&pdev->dev, riic_reset_control_assert, rstc); + if (ret) + return ret; for (i = 0; i < ARRAY_SIZE(riic_irqs); i++) { ret = platform_get_irq(pdev, riic_irqs[i].res_num); @@ -492,8 +493,7 @@ static int riic_i2c_remove(struct platform_device *pdev) } static const struct of_device_id riic_i2c_dt_ids[] = { - { .compatible = "renesas,riic-r9a07g044", .data = (void *)RIIC_RZ_G2L }, - { .compatible = "renesas,riic-rz", .data = (void *)RIIC_RZ_A }, + { .compatible = "renesas,riic-rz", }, { /* Sentinel */ }, }; -- cgit v1.2.3 From d8703554f4dea9775417525b22b3d65ed1c6b16e Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 21 Feb 2022 15:57:16 +0530 Subject: i2c: qcom-geni: Add support for GPI DMA QUP Serial engines supports data transfers thru FIFO mode, SE DMA mode and lastly GPI DMA mode. Former two are already supported and this adds supports for the last mode. In GPI DMA mode, the firmware is issued commands by driver to perform DMA and setup the serial port. Signed-off-by: Vinod Koul Reviewed-by: Bjorn Andersson Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-qcom-geni.c | 308 +++++++++++++++++++++++++++++++++---- 1 file changed, 280 insertions(+), 28 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 6d635a7c104c..fc1dcc19f2a1 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -3,7 +3,9 @@ #include #include +#include #include +#include #include #include #include @@ -48,6 +50,9 @@ #define LOW_COUNTER_SHFT 10 #define CYCLE_COUNTER_MSK GENMASK(9, 0) +#define I2C_PACK_TX BIT(0) +#define I2C_PACK_RX BIT(1) + enum geni_i2c_err_code { GP_IRQ0, NACK, @@ -89,6 +94,9 @@ struct geni_i2c_dev { void *dma_buf; size_t xfer_len; dma_addr_t dma_addr; + struct dma_chan *tx_c; + struct dma_chan *rx_c; + bool gpi_mode; }; struct geni_i2c_err_log { @@ -456,12 +464,207 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, return gi2c->err; } +static void i2c_gpi_cb_result(void *cb, const struct dmaengine_result *result) +{ + struct geni_i2c_dev *gi2c = cb; + + if (result->result != DMA_TRANS_NOERROR) { + dev_err(gi2c->se.dev, "DMA txn failed:%d\n", result->result); + gi2c->err = -EIO; + } else if (result->residue) { + dev_dbg(gi2c->se.dev, "DMA xfer has pending: %d\n", result->residue); + } + + complete(&gi2c->done); +} + +static void geni_i2c_gpi_unmap(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, + void *tx_buf, dma_addr_t tx_addr, + void *rx_buf, dma_addr_t rx_addr) +{ + if (tx_buf) { + dma_unmap_single(gi2c->se.dev->parent, tx_addr, msg->len, DMA_TO_DEVICE); + i2c_put_dma_safe_msg_buf(tx_buf, msg, false); + } + + if (rx_buf) { + dma_unmap_single(gi2c->se.dev->parent, rx_addr, msg->len, DMA_FROM_DEVICE); + i2c_put_dma_safe_msg_buf(rx_buf, msg, false); + } +} + +static int geni_i2c_gpi(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, + struct dma_slave_config *config, dma_addr_t *dma_addr_p, + void **buf, unsigned int op, struct dma_chan *dma_chan) +{ + struct gpi_i2c_config *peripheral; + unsigned int flags; + void *dma_buf; + dma_addr_t addr; + enum dma_data_direction map_dirn; + enum dma_transfer_direction dma_dirn; + struct dma_async_tx_descriptor *desc; + int ret; + + peripheral = config->peripheral_config; + + dma_buf = i2c_get_dma_safe_msg_buf(msg, 1); + if (!dma_buf) + return -ENOMEM; + + if (op == I2C_WRITE) + map_dirn = DMA_TO_DEVICE; + else + map_dirn = DMA_FROM_DEVICE; + + addr = dma_map_single(gi2c->se.dev->parent, dma_buf, msg->len, map_dirn); + if (dma_mapping_error(gi2c->se.dev->parent, addr)) { + i2c_put_dma_safe_msg_buf(dma_buf, msg, false); + return -ENOMEM; + } + + /* set the length as message for rx txn */ + peripheral->rx_len = msg->len; + peripheral->op = op; + + ret = dmaengine_slave_config(dma_chan, config); + if (ret) { + dev_err(gi2c->se.dev, "dma config error: %d for op:%d\n", ret, op); + goto err_config; + } + + peripheral->set_config = 0; + peripheral->multi_msg = true; + flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + + if (op == I2C_WRITE) + dma_dirn = DMA_MEM_TO_DEV; + else + dma_dirn = DMA_DEV_TO_MEM; + + desc = dmaengine_prep_slave_single(dma_chan, addr, msg->len, dma_dirn, flags); + if (!desc) { + dev_err(gi2c->se.dev, "prep_slave_sg failed\n"); + ret = -EIO; + goto err_config; + } + + desc->callback_result = i2c_gpi_cb_result; + desc->callback_param = gi2c; + + dmaengine_submit(desc); + *dma_addr_p = addr; + + return 0; + +err_config: + dma_unmap_single(gi2c->se.dev->parent, addr, msg->len, map_dirn); + i2c_put_dma_safe_msg_buf(dma_buf, msg, false); + return ret; +} + +static int geni_i2c_gpi_xfer(struct geni_i2c_dev *gi2c, struct i2c_msg msgs[], int num) +{ + struct dma_slave_config config = {}; + struct gpi_i2c_config peripheral = {}; + int i, ret = 0, timeout; + dma_addr_t tx_addr, rx_addr; + void *tx_buf = NULL, *rx_buf = NULL; + const struct geni_i2c_clk_fld *itr = gi2c->clk_fld; + + config.peripheral_config = &peripheral; + config.peripheral_size = sizeof(peripheral); + + peripheral.pack_enable = I2C_PACK_TX | I2C_PACK_RX; + peripheral.cycle_count = itr->t_cycle_cnt; + peripheral.high_count = itr->t_high_cnt; + peripheral.low_count = itr->t_low_cnt; + peripheral.clk_div = itr->clk_div; + peripheral.set_config = 1; + peripheral.multi_msg = false; + + for (i = 0; i < num; i++) { + gi2c->cur = &msgs[i]; + gi2c->err = 0; + dev_dbg(gi2c->se.dev, "msg[%d].len:%d\n", i, gi2c->cur->len); + + peripheral.stretch = 0; + if (i < num - 1) + peripheral.stretch = 1; + + peripheral.addr = msgs[i].addr; + + if (msgs[i].flags & I2C_M_RD) { + ret = geni_i2c_gpi(gi2c, &msgs[i], &config, + &rx_addr, &rx_buf, I2C_READ, gi2c->rx_c); + if (ret) + goto err; + } + + ret = geni_i2c_gpi(gi2c, &msgs[i], &config, + &tx_addr, &tx_buf, I2C_WRITE, gi2c->tx_c); + if (ret) + goto err; + + if (msgs[i].flags & I2C_M_RD) + dma_async_issue_pending(gi2c->rx_c); + dma_async_issue_pending(gi2c->tx_c); + + timeout = wait_for_completion_timeout(&gi2c->done, XFER_TIMEOUT); + if (!timeout) { + dev_err(gi2c->se.dev, "I2C timeout gpi flags:%d addr:0x%x\n", + gi2c->cur->flags, gi2c->cur->addr); + gi2c->err = -ETIMEDOUT; + goto err; + } + + if (gi2c->err) { + ret = gi2c->err; + goto err; + } + + geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr); + } + + return num; + +err: + dev_err(gi2c->se.dev, "GPI transfer failed: %d\n", ret); + dmaengine_terminate_sync(gi2c->rx_c); + dmaengine_terminate_sync(gi2c->tx_c); + geni_i2c_gpi_unmap(gi2c, &msgs[i], tx_buf, tx_addr, rx_buf, rx_addr); + return ret; +} + +static int geni_i2c_fifo_xfer(struct geni_i2c_dev *gi2c, + struct i2c_msg msgs[], int num) +{ + int i, ret = 0; + + for (i = 0; i < num; i++) { + u32 m_param = i < (num - 1) ? STOP_STRETCH : 0; + + m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK); + + gi2c->cur = &msgs[i]; + if (msgs[i].flags & I2C_M_RD) + ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param); + else + ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param); + + if (ret) + return ret; + } + + return num; +} + static int geni_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap); - int i, ret; + int ret; gi2c->err = 0; reinit_completion(&gi2c->done); @@ -475,28 +678,17 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, } qcom_geni_i2c_conf(gi2c); - for (i = 0; i < num; i++) { - u32 m_param = i < (num - 1) ? STOP_STRETCH : 0; - - m_param |= ((msgs[i].addr << SLV_ADDR_SHFT) & SLV_ADDR_MSK); - - gi2c->cur = &msgs[i]; - if (msgs[i].flags & I2C_M_RD) - ret = geni_i2c_rx_one_msg(gi2c, &msgs[i], m_param); - else - ret = geni_i2c_tx_one_msg(gi2c, &msgs[i], m_param); - if (ret) - break; - } - if (ret == 0) - ret = num; + if (gi2c->gpi_mode) + ret = geni_i2c_gpi_xfer(gi2c, msgs, num); + else + ret = geni_i2c_fifo_xfer(gi2c, msgs, num); pm_runtime_mark_last_busy(gi2c->se.dev); pm_runtime_put_autosuspend(gi2c->se.dev); gi2c->cur = NULL; gi2c->err = 0; - return ret; + return num; } static u32 geni_i2c_func(struct i2c_adapter *adap) @@ -517,11 +709,50 @@ static const struct acpi_device_id geni_i2c_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, geni_i2c_acpi_match); #endif +static void release_gpi_dma(struct geni_i2c_dev *gi2c) +{ + if (gi2c->rx_c) + dma_release_channel(gi2c->rx_c); + + if (gi2c->tx_c) + dma_release_channel(gi2c->tx_c); +} + +static int setup_gpi_dma(struct geni_i2c_dev *gi2c) +{ + int ret; + + geni_se_select_mode(&gi2c->se, GENI_GPI_DMA); + gi2c->tx_c = dma_request_chan(gi2c->se.dev, "tx"); + if (IS_ERR(gi2c->tx_c)) { + ret = dev_err_probe(gi2c->se.dev, PTR_ERR(gi2c->tx_c), + "Failed to get tx DMA ch\n"); + if (ret < 0) + goto err_tx; + } + + gi2c->rx_c = dma_request_chan(gi2c->se.dev, "rx"); + if (IS_ERR(gi2c->rx_c)) { + ret = dev_err_probe(gi2c->se.dev, PTR_ERR(gi2c->rx_c), + "Failed to get rx DMA ch\n"); + if (ret < 0) + goto err_rx; + } + + dev_dbg(gi2c->se.dev, "Grabbed GPI dma channels\n"); + return 0; + +err_rx: + dma_release_channel(gi2c->tx_c); +err_tx: + return ret; +} + static int geni_i2c_probe(struct platform_device *pdev) { struct geni_i2c_dev *gi2c; struct resource *res; - u32 proto, tx_depth; + u32 proto, tx_depth, fifo_disable; int ret; struct device *dev = &pdev->dev; @@ -601,27 +832,43 @@ static int geni_i2c_probe(struct platform_device *pdev) return ret; } proto = geni_se_read_proto(&gi2c->se); - tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se); if (proto != GENI_SE_I2C) { dev_err(dev, "Invalid proto %d\n", proto); geni_se_resources_off(&gi2c->se); return -ENXIO; } - gi2c->tx_wm = tx_depth - 1; - geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth); - geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW, - true, true, true); + + fifo_disable = readl_relaxed(gi2c->se.base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE; + if (fifo_disable) { + /* FIFO is disabled, so we can only use GPI DMA */ + gi2c->gpi_mode = true; + ret = setup_gpi_dma(gi2c); + if (ret) { + dev_err(dev, "Failed to setup GPI DMA mode:%d ret\n", ret); + return ret; + } + + dev_dbg(dev, "Using GPI DMA mode for I2C\n"); + } else { + gi2c->gpi_mode = false; + tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se); + gi2c->tx_wm = tx_depth - 1; + geni_se_init(&gi2c->se, gi2c->tx_wm, tx_depth); + geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, + PACKING_BYTES_PW, true, true, true); + + dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth); + } + ret = geni_se_resources_off(&gi2c->se); if (ret) { dev_err(dev, "Error turning off resources %d\n", ret); - return ret; + goto err_dma; } ret = geni_icc_disable(&gi2c->se); if (ret) - return ret; - - dev_dbg(dev, "i2c fifo/se-dma mode. fifo depth:%d\n", tx_depth); + goto err_dma; gi2c->suspended = 1; pm_runtime_set_suspended(gi2c->se.dev); @@ -633,12 +880,16 @@ static int geni_i2c_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "Error adding i2c adapter %d\n", ret); pm_runtime_disable(gi2c->se.dev); - return ret; + goto err_dma; } dev_dbg(dev, "Geni-I2C adaptor successfully added\n"); return 0; + +err_dma: + release_gpi_dma(gi2c); + return ret; } static int geni_i2c_remove(struct platform_device *pdev) @@ -646,6 +897,7 @@ static int geni_i2c_remove(struct platform_device *pdev) struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev); i2c_del_adapter(&gi2c->adap); + release_gpi_dma(gi2c); pm_runtime_disable(gi2c->se.dev); return 0; } -- cgit v1.2.3 From 17ba1e87fca9e6bad7eacbb1ef042c83358d245e Mon Sep 17 00:00:00 2001 From: Jan Dabros Date: Fri, 18 Feb 2022 14:33:48 +0100 Subject: i2c: designware: Fix improper usage of readl Kernel test robot reported incorrect type in argument 1 of readl(), but more importantly it brought attention that MMIO accessor shouldn't be used in this case, since req->hdr.status is part of a command-response buffer in system memory. Since its value may be altered by PSP outside of the scope of current thread (somehow similar to IRQ handler case), we need to use READ_ONCE() to ensure compiler won't optimize this call. Fix also 'status' variable type to reflect that corresponding field in command-response buffer is platform-independent u32. Fixes: 78d5e9e299e3 ("i2c: designware: Add AMD PSP I2C bus support") Signed-off-by: Jan Dabros Reported-by: kernel test robot Reviewed-by: Andy Shevchenko Acked-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-amdpsp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-amdpsp.c b/drivers/i2c/busses/i2c-designware-amdpsp.c index 9d37becbd846..3844695e6bf3 100644 --- a/drivers/i2c/busses/i2c-designware-amdpsp.c +++ b/drivers/i2c/busses/i2c-designware-amdpsp.c @@ -160,9 +160,10 @@ static int psp_send_cmd(struct psp_i2c_req *req) /* Helper to verify status returned by PSP */ static int check_i2c_req_sts(struct psp_i2c_req *req) { - int status; + u32 status; - status = readl(&req->hdr.status); + /* Status field in command-response buffer is updated by PSP */ + status = READ_ONCE(req->hdr.status); switch (status) { case PSP_I2C_REQ_STS_OK: -- cgit v1.2.3 From b5a796c6a8091b636f12cd9df12ccb2eed1b7f6c Mon Sep 17 00:00:00 2001 From: Kewei Xu Date: Thu, 17 Feb 2022 20:22:43 +0800 Subject: i2c: mediatek: modify bus speed calculation formula When clock-div is 0 or greater than 1, the bus speed calculated by the old speed calculation formula will be larger than the target speed. So we update the formula. Signed-off-by: Kewei Xu Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Qii Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 51 +++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 10 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index aa4d21838e14..682293ec9825 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -67,11 +67,12 @@ #define MAX_SAMPLE_CNT_DIV 8 #define MAX_STEP_CNT_DIV 64 -#define MAX_CLOCK_DIV 256 +#define MAX_CLOCK_DIV_8BITS 256 +#define MAX_CLOCK_DIV_5BITS 32 #define MAX_HS_STEP_CNT_DIV 8 -#define I2C_STANDARD_MODE_BUFFER (1000 / 2) -#define I2C_FAST_MODE_BUFFER (300 / 2) -#define I2C_FAST_MODE_PLUS_BUFFER (20 / 2) +#define I2C_STANDARD_MODE_BUFFER (1000 / 3) +#define I2C_FAST_MODE_BUFFER (300 / 3) +#define I2C_FAST_MODE_PLUS_BUFFER (20 / 3) #define I2C_CONTROL_RS (0x1 << 1) #define I2C_CONTROL_DMA_EN (0x1 << 2) @@ -604,6 +605,31 @@ static int mtk_i2c_max_step_cnt(unsigned int target_speed) return MAX_STEP_CNT_DIV; } +static int mtk_i2c_get_clk_div_restri(struct mtk_i2c *i2c, + unsigned int sample_cnt) +{ + int clk_div_restri = 0; + + if (i2c->dev_comp->ltiming_adjust == 0) + return 0; + + if (sample_cnt == 1) { + if (i2c->ac_timing.inter_clk_div == 0) + clk_div_restri = 0; + else + clk_div_restri = 1; + } else { + if (i2c->ac_timing.inter_clk_div == 0) + clk_div_restri = -1; + else if (i2c->ac_timing.inter_clk_div == 1) + clk_div_restri = 0; + else + clk_div_restri = 1; + } + + return clk_div_restri; +} + /* * Check and Calculate i2c ac-timing * @@ -732,6 +758,7 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src, unsigned int best_mul; unsigned int cnt_mul; int ret = -EINVAL; + int clk_div_restri = 0; if (target_speed > I2C_MAX_HIGH_SPEED_MODE_FREQ) target_speed = I2C_MAX_HIGH_SPEED_MODE_FREQ; @@ -749,7 +776,8 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src, * optimizing for sample_cnt * step_cnt being minimal */ for (sample_cnt = 1; sample_cnt <= MAX_SAMPLE_CNT_DIV; sample_cnt++) { - step_cnt = DIV_ROUND_UP(opt_div, sample_cnt); + clk_div_restri = mtk_i2c_get_clk_div_restri(i2c, sample_cnt); + step_cnt = DIV_ROUND_UP(opt_div + clk_div_restri, sample_cnt); cnt_mul = step_cnt * sample_cnt; if (step_cnt > max_step_cnt) continue; @@ -763,7 +791,7 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src, best_mul = cnt_mul; base_sample_cnt = sample_cnt; base_step_cnt = step_cnt; - if (best_mul == opt_div) + if (best_mul == (opt_div + clk_div_restri)) break; } } @@ -774,7 +802,8 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src, sample_cnt = base_sample_cnt; step_cnt = base_step_cnt; - if ((clk_src / (2 * sample_cnt * step_cnt)) > target_speed) { + if ((clk_src / (2 * (sample_cnt * step_cnt - clk_div_restri))) > + target_speed) { /* In this case, hardware can't support such * low i2c_bus_freq */ @@ -803,13 +832,16 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk) target_speed = i2c->speed_hz; parent_clk /= i2c->clk_src_div; - if (i2c->dev_comp->timing_adjust) - max_clk_div = MAX_CLOCK_DIV; + if (i2c->dev_comp->timing_adjust && i2c->dev_comp->ltiming_adjust) + max_clk_div = MAX_CLOCK_DIV_5BITS; + else if (i2c->dev_comp->timing_adjust) + max_clk_div = MAX_CLOCK_DIV_8BITS; else max_clk_div = 1; for (clk_div = 1; clk_div <= max_clk_div; clk_div++) { clk_src = parent_clk / clk_div; + i2c->ac_timing.inter_clk_div = clk_div - 1; if (target_speed > I2C_MAX_FAST_MODE_PLUS_FREQ) { /* Set master code speed register */ @@ -856,7 +888,6 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk) break; } - i2c->ac_timing.inter_clk_div = clk_div - 1; return 0; } -- cgit v1.2.3 From 5b9df0acd22a0bcd4e0e97e6f8368c31843ceb95 Mon Sep 17 00:00:00 2001 From: Xu Wang Date: Wed, 30 Sep 2020 08:42:33 +0000 Subject: i2c: mediatek: remove redundant null check Because clk_disable_unprepare already checked NULL clock parameter, so the additional checks are unnecessary, just remove it Signed-off-by: Xu Wang Reviewed-by: Qii Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 682293ec9825..5bf7f965800d 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -476,8 +476,7 @@ static int mtk_i2c_clock_enable(struct mtk_i2c *i2c) return 0; err_arb: - if (i2c->have_pmic) - clk_disable_unprepare(i2c->clk_pmic); + clk_disable_unprepare(i2c->clk_pmic); err_pmic: clk_disable_unprepare(i2c->clk_main); err_main: @@ -488,11 +487,9 @@ err_main: static void mtk_i2c_clock_disable(struct mtk_i2c *i2c) { - if (i2c->clk_arb) - clk_disable_unprepare(i2c->clk_arb); + clk_disable_unprepare(i2c->clk_arb); - if (i2c->have_pmic) - clk_disable_unprepare(i2c->clk_pmic); + clk_disable_unprepare(i2c->clk_pmic); clk_disable_unprepare(i2c->clk_main); clk_disable_unprepare(i2c->clk_dma); -- cgit v1.2.3 From c57813b8b288dcb5e158993cfdff31a60bc24955 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 23 Feb 2022 14:48:38 +0100 Subject: i2c: designware: Lock the adapter while setting the suspended flag Lock the adapter while setting the suspended flag, to ensure that other locked code always sees the change immediately, rather then possibly using a stale value. This involves splitting the suspend/resume callbacks into separate runtime and normal suspend/resume calls. This is necessary because i2c_dw_xfer() will get called by the i2c-core with the adapter locked and it in turn calls the runtime-resume callback through pm_runtime_get_sync(). So the runtime versions of the suspend/resume callbacks cannot take the adapter-lock. Note this patch simply makes the runtime suspend/resume callbacks not deal with the suspended flag at all. During runtime the pm_runtime_get_sync() from i2c_dw_xfer() will always ensure that the adapter is resumed when necessary. The suspended flag check is only necessary to check proper suspend/resume ordering during normal suspend/resume which makes the pm_runtime_get_sync() call a no-op. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Acked-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-pcidrv.c | 31 ++++++++++++++++++++++++----- drivers/i2c/busses/i2c-designware-platdrv.c | 31 ++++++++++++++++++++++++----- 2 files changed, 52 insertions(+), 10 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 2782dddfb087..a736a2aaa66e 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -194,14 +194,30 @@ static struct dw_pci_controller dw_pci_controllers[] = { }, }; +static int __maybe_unused i2c_dw_pci_runtime_suspend(struct device *dev) +{ + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); + + i_dev->disable(i_dev); + return 0; +} + static int __maybe_unused i2c_dw_pci_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); + i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); i_dev->suspended = true; - i_dev->disable(i_dev); + i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - return 0; + return i2c_dw_pci_runtime_suspend(dev); +} + +static int __maybe_unused i2c_dw_pci_runtime_resume(struct device *dev) +{ + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); + + return i_dev->init(i_dev); } static int __maybe_unused i2c_dw_pci_resume(struct device *dev) @@ -209,14 +225,19 @@ static int __maybe_unused i2c_dw_pci_resume(struct device *dev) struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); int ret; - ret = i_dev->init(i_dev); + ret = i2c_dw_pci_runtime_resume(dev); + + i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); i_dev->suspended = false; + i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); return ret; } -static UNIVERSAL_DEV_PM_OPS(i2c_dw_pm_ops, i2c_dw_pci_suspend, - i2c_dw_pci_resume, NULL); +static const struct dev_pm_ops i2c_dw_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(i2c_dw_pci_suspend, i2c_dw_pci_resume) + SET_RUNTIME_PM_OPS(i2c_dw_pci_runtime_suspend, i2c_dw_pci_runtime_resume, NULL) +}; static int i2c_dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 9973ac894a51..f0703286d93b 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -428,12 +428,10 @@ static void dw_i2c_plat_complete(struct device *dev) #endif #ifdef CONFIG_PM -static int dw_i2c_plat_suspend(struct device *dev) +static int dw_i2c_plat_runtime_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - i_dev->suspended = true; - if (i_dev->shared_with_punit) return 0; @@ -443,7 +441,18 @@ static int dw_i2c_plat_suspend(struct device *dev) return 0; } -static int dw_i2c_plat_resume(struct device *dev) +static int dw_i2c_plat_suspend(struct device *dev) +{ + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); + + i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i_dev->suspended = true; + i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + + return dw_i2c_plat_runtime_suspend(dev); +} + +static int dw_i2c_plat_runtime_resume(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); @@ -451,7 +460,19 @@ static int dw_i2c_plat_resume(struct device *dev) i2c_dw_prepare_clk(i_dev, true); i_dev->init(i_dev); + + return 0; +} + +static int dw_i2c_plat_resume(struct device *dev) +{ + struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); + + dw_i2c_plat_runtime_resume(dev); + + i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); i_dev->suspended = false; + i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); return 0; } @@ -460,7 +481,7 @@ static const struct dev_pm_ops dw_i2c_dev_pm_ops = { .prepare = dw_i2c_plat_prepare, .complete = dw_i2c_plat_complete, SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) - SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL) + SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL) }; #define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops) -- cgit v1.2.3 From 80704a84a9f8276337a83c0c3ce641af3b27c79c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 23 Feb 2022 14:48:39 +0100 Subject: i2c: designware: Use the i2c_mark_adapter_suspended/resumed() helpers Use the i2c_mark_adapter_suspended/resumed() i2c-core helpers and rely on the i2c-core's suspended checking instead of using DIY code. Signed-off-by: Hans de Goede Acked-by: Jarkko Nikula Reviewed-by: Andy Shevchenko Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-core.h | 2 -- drivers/i2c/busses/i2c-designware-master.c | 5 ----- drivers/i2c/busses/i2c-designware-pcidrv.c | 8 ++------ drivers/i2c/busses/i2c-designware-platdrv.c | 9 ++------- 4 files changed, 4 insertions(+), 20 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 1d65212fddbd..70b80e710990 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -236,7 +236,6 @@ struct reset_control; * @set_sda_hold_time: callback to retrieve IP specific SDA hold timing * @mode: operation mode - DW_IC_MASTER or DW_IC_SLAVE * @rinfo: I²C GPIO recovery information - * @suspended: set to true if the controller is suspended * * HCNT and LCNT parameters can be used if the platform knows more accurate * values than the one computed based only on the input clock frequency. @@ -295,7 +294,6 @@ struct dw_i2c_dev { int (*set_sda_hold_time)(struct dw_i2c_dev *dev); int mode; struct i2c_bus_recovery_info rinfo; - bool suspended; }; #define ACCESS_INTR_MASK BIT(0) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 1a4b23556db3..44a94b225ed8 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -567,11 +567,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) goto done_nolock; } - if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) { - ret = -ESHUTDOWN; - goto done_nolock; - } - reinit_completion(&dev->cmd_complete); dev->msgs = msgs; dev->msgs_num = num; diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index a736a2aaa66e..608e61209455 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -206,9 +206,7 @@ static int __maybe_unused i2c_dw_pci_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - i_dev->suspended = true; - i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i2c_mark_adapter_suspended(&i_dev->adapter); return i2c_dw_pci_runtime_suspend(dev); } @@ -227,9 +225,7 @@ static int __maybe_unused i2c_dw_pci_resume(struct device *dev) ret = i2c_dw_pci_runtime_resume(dev); - i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - i_dev->suspended = false; - i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i2c_mark_adapter_resumed(&i_dev->adapter); return ret; } diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index f0703286d93b..116a297d1f6b 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -445,9 +445,7 @@ static int dw_i2c_plat_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); - i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - i_dev->suspended = true; - i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i2c_mark_adapter_suspended(&i_dev->adapter); return dw_i2c_plat_runtime_suspend(dev); } @@ -469,10 +467,7 @@ static int dw_i2c_plat_resume(struct device *dev) struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); dw_i2c_plat_runtime_resume(dev); - - i2c_lock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); - i_dev->suspended = false; - i2c_unlock_bus(&i_dev->adapter, I2C_LOCK_ROOT_ADAPTER); + i2c_mark_adapter_resumed(&i_dev->adapter); return 0; } -- cgit v1.2.3 From d7583c8a57485da19feb6dd85573763a8c5ec1d1 Mon Sep 17 00:00:00 2001 From: Akhil R Date: Thu, 10 Feb 2022 21:06:03 +0530 Subject: i2c: tegra: Add SMBus block read function Emulate SMBus block read using ContinueXfer to read the length byte Signed-off-by: Akhil R Reviewed-by: Dmitry Osipenko Acked-by: Thierry Reding Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-tegra.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index 03cea102ab76..2941e42aa6a0 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -1233,6 +1233,11 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev, return err; i2c_dev->msg_buf = msg->buf; + + /* The condition true implies smbus block read and len is already read */ + if (msg->flags & I2C_M_RECV_LEN && end_state != MSG_END_CONTINUE) + i2c_dev->msg_buf = msg->buf + 1; + i2c_dev->msg_buf_remaining = msg->len; i2c_dev->msg_err = I2C_ERR_NONE; i2c_dev->msg_read = !!(msg->flags & I2C_M_RD); @@ -1389,6 +1394,15 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], else end_type = MSG_END_REPEAT_START; } + /* If M_RECV_LEN use ContinueXfer to read the first byte */ + if (msgs[i].flags & I2C_M_RECV_LEN) { + ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], MSG_END_CONTINUE); + if (ret) + break; + /* Set the read byte as msg len */ + msgs[i].len = msgs[i].buf[0]; + dev_dbg(i2c_dev->dev, "reading %d bytes\n", msgs[i].len); + } ret = tegra_i2c_xfer_msg(i2c_dev, &msgs[i], end_type); if (ret) break; @@ -1416,10 +1430,10 @@ static u32 tegra_i2c_func(struct i2c_adapter *adap) { struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap); u32 ret = I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | - I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; + I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; if (i2c_dev->hw->has_continue_xfer_support) - ret |= I2C_FUNC_NOSTART; + ret |= I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_READ_BLOCK_DATA; return ret; } -- cgit v1.2.3 From 9c02c6391c88a9d2669c2ff8da92cf6efd7f760b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 1 Mar 2022 17:28:59 +0100 Subject: i2c: i801: Drop useless masking in i801_access I went all the way back to kernel 2.6.12 and that masking was never needed. I suppose it was there in anticipation of software PEC support, but that was never added to the driver (and never will be, as this is made obsolete by hardware PEC). I'm also removing initialization to 0, which is not needed either, and would prevent the compiler from reporting an actual usage of uninitialized variables. Signed-off-by: Jean Delvare Reviewed-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-i801.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 36b086ef1378..715f6d51d09c 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -792,7 +792,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, { int hwpec; int block = 0; - int ret = 0, xact = 0; + int ret, xact; struct i801_priv *priv = i2c_get_adapdata(adap); mutex_lock(&priv->acpi_lock); @@ -904,7 +904,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) goto out; - switch (xact & 0x7f) { + switch (xact) { case I801_BYTE: /* Result put in SMBHSTDAT0 */ case I801_BYTE_DATA: data->byte = inb_p(SMBHSTDAT0(priv)); -- cgit v1.2.3 From 55b6f82e9443aaf0c6c3a27c394481555ed0da50 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 1 Mar 2022 17:39:37 +0100 Subject: i2c: i801: Add support for the Process Call command The Process Call command is implemented by the hardware since the very first Intel 82801 chipset, and trivial to support. Oscar Romero Matamala from the Georgia Institute of Technology told me it is needed for an experiment his team is working on at the moment, so let's just add support for it. Signed-off-by: Jean Delvare Reviewed-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-i801.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 715f6d51d09c..1fc006a1d532 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -166,7 +166,7 @@ #define I801_BYTE 0x04 #define I801_BYTE_DATA 0x08 #define I801_WORD_DATA 0x0C -#define I801_PROC_CALL 0x10 /* unimplemented */ +#define I801_PROC_CALL 0x10 #define I801_BLOCK_DATA 0x14 #define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */ #define I801_BLOCK_PROC_CALL 0x1C @@ -838,6 +838,14 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, } xact = I801_WORD_DATA; break; + case I2C_SMBUS_PROC_CALL: + outb_p((addr & 0x7f) << 1, SMBHSTADD(priv)); + outb_p(command, SMBHSTCMD(priv)); + outb_p(data->word & 0xff, SMBHSTDAT0(priv)); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv)); + xact = I801_PROC_CALL; + read_write = I2C_SMBUS_READ; + break; case I2C_SMBUS_BLOCK_DATA: outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); @@ -910,6 +918,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, data->byte = inb_p(SMBHSTDAT0(priv)); break; case I801_WORD_DATA: + case I801_PROC_CALL: data->word = inb_p(SMBHSTDAT0(priv)) + (inb_p(SMBHSTDAT1(priv)) << 8); break; @@ -935,6 +944,7 @@ static u32 i801_func(struct i2c_adapter *adapter) return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL | I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | ((priv->features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) | ((priv->features & FEATURE_BLOCK_PROC) ? -- cgit v1.2.3 From 1d366c2f9df8279df2adbb60471f86fc40a1c39e Mon Sep 17 00:00:00 2001 From: Robert Hancock Date: Thu, 27 Jan 2022 11:50:13 -0600 Subject: i2c: xiic: Make bus names unique This driver is for an FPGA logic core, so there can be arbitrarily many instances of the bus on a given system. Previously all of the I2C bus names were "xiic-i2c" which caused issues with lm_sensors when trying to map human-readable names to sensor inputs because it could not properly distinguish the busses, for example. Append the platform device name to the I2C bus name so it is unique between different instances. Fixes: e1d5b6598cdc ("i2c: Add support for Xilinx XPS IIC Bus Interface") Signed-off-by: Robert Hancock Tested-by: Michal Simek Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-xiic.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index eb789cfb9973..ffefe3c482e9 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -734,7 +734,6 @@ static const struct i2c_adapter_quirks xiic_quirks = { static const struct i2c_adapter xiic_adapter = { .owner = THIS_MODULE, - .name = DRIVER_NAME, .class = I2C_CLASS_DEPRECATED, .algo = &xiic_algorithm, .quirks = &xiic_quirks, @@ -771,6 +770,8 @@ static int xiic_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(&i2c->adap, i2c); i2c->adap.dev.parent = &pdev->dev; i2c->adap.dev.of_node = pdev->dev.of_node; + snprintf(i2c->adap.name, sizeof(i2c->adap.name), + DRIVER_NAME " %s", pdev->name); mutex_init(&i2c->lock); -- cgit v1.2.3 From 52c74d17d89c6e69f9b774cdd477517e62383f55 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 3 Mar 2022 17:42:56 +0100 Subject: i2c: i801: Drop two outdated comments * Timeouts are reported even in interrupt mode since commit b3b8df97723d ("i2c: i801: Use wait_event_timeout to wait for interrupts") so drop the comment which claims this only happens in polled mode. * xact does not include the PEC bit, as the driver does not support software PEC. Signed-off-by: Jean Delvare Reviewed-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-i801.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 1fc006a1d532..ff706349bdfb 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -364,9 +364,6 @@ static int i801_check_post(struct i801_priv *priv, int status) /* * If the SMBus is still busy, we give up - * Note: This timeout condition only happens when using polling - * transactions. For interrupt operation, NAK/timeout is indicated by - * DEV_ERR. */ if (unlikely(status < 0)) { dev_err(&priv->pci_dev->dev, "Transaction timeout\n"); @@ -475,8 +472,6 @@ static int i801_transaction(struct i801_priv *priv, int xact) return i801_check_post(priv, result ? priv->status : -ETIMEDOUT); } - /* the current contents of SMBHSTCNT can be overwritten, since PEC, - * SMBSCMD are passed in xact */ outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv)); status = i801_wait_intr(priv); -- cgit v1.2.3 From 0016a32f7591aec5413cf1f52b9ce522f1cae44f Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Thu, 3 Mar 2022 10:15:47 +0100 Subject: i2c: mt65xx: Simplify with clk-bulk Since depending on the SoC or specific bus functionality some clocks may be optional, we cannot get the benefit of using devm_clk_bulk_get() but, by migrating to clk-bulk, we are able to remove the custom functions mtk_i2c_clock_enable() and mtk_i2c_clock_disable(), increasing common APIs usage, hence (lightly) decreasing kernel footprint. Signed-off-by: AngeloGioacchino Del Regno Reviewed-by: Qii Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 124 +++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 73 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 5bf7f965800d..7dde3b64766b 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -86,6 +86,27 @@ #define I2C_DRV_NAME "i2c-mt65xx" +/** + * enum i2c_mt65xx_clks - Clocks enumeration for MT65XX I2C + * + * @I2C_MT65XX_CLK_MAIN: main clock for i2c bus + * @I2C_MT65XX_CLK_DMA: DMA clock for i2c via DMA + * @I2C_MT65XX_CLK_PMIC: PMIC clock for i2c from PMIC + * @I2C_MT65XX_CLK_ARB: Arbitrator clock for i2c + * @I2C_MT65XX_CLK_MAX: Number of supported clocks + */ +enum i2c_mt65xx_clks { + I2C_MT65XX_CLK_MAIN = 0, + I2C_MT65XX_CLK_DMA, + I2C_MT65XX_CLK_PMIC, + I2C_MT65XX_CLK_ARB, + I2C_MT65XX_CLK_MAX +}; + +static const char * const i2c_mt65xx_clk_ids[I2C_MT65XX_CLK_MAX] = { + "main", "dma", "pmic", "arb" +}; + enum DMA_REGS_OFFSET { OFFSET_INT_FLAG = 0x0, OFFSET_INT_EN = 0x04, @@ -244,10 +265,7 @@ struct mtk_i2c { /* set in i2c probe */ void __iomem *base; /* i2c base addr */ void __iomem *pdmabase; /* dma base address*/ - struct clk *clk_main; /* main clock for i2c bus */ - struct clk *clk_dma; /* DMA clock for i2c via DMA */ - struct clk *clk_pmic; /* PMIC clock for i2c from PMIC */ - struct clk *clk_arb; /* Arbitrator clock for i2c */ + struct clk_bulk_data clocks[I2C_MT65XX_CLK_MAX]; /* clocks for i2c */ bool have_pmic; /* can use i2c pins from PMIC */ bool use_push_pull; /* IO config push-pull mode */ @@ -449,52 +467,6 @@ static void mtk_i2c_writew(struct mtk_i2c *i2c, u16 val, writew(val, i2c->base + i2c->dev_comp->regs[reg]); } -static int mtk_i2c_clock_enable(struct mtk_i2c *i2c) -{ - int ret; - - ret = clk_prepare_enable(i2c->clk_dma); - if (ret) - return ret; - - ret = clk_prepare_enable(i2c->clk_main); - if (ret) - goto err_main; - - if (i2c->have_pmic) { - ret = clk_prepare_enable(i2c->clk_pmic); - if (ret) - goto err_pmic; - } - - if (i2c->clk_arb) { - ret = clk_prepare_enable(i2c->clk_arb); - if (ret) - goto err_arb; - } - - return 0; - -err_arb: - clk_disable_unprepare(i2c->clk_pmic); -err_pmic: - clk_disable_unprepare(i2c->clk_main); -err_main: - clk_disable_unprepare(i2c->clk_dma); - - return ret; -} - -static void mtk_i2c_clock_disable(struct mtk_i2c *i2c) -{ - clk_disable_unprepare(i2c->clk_arb); - - clk_disable_unprepare(i2c->clk_pmic); - - clk_disable_unprepare(i2c->clk_main); - clk_disable_unprepare(i2c->clk_dma); -} - static void mtk_i2c_init_hw(struct mtk_i2c *i2c) { u16 control_reg; @@ -1191,7 +1163,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, int left_num = num; struct mtk_i2c *i2c = i2c_get_adapdata(adap); - ret = mtk_i2c_clock_enable(i2c); + ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); if (ret) return ret; @@ -1245,7 +1217,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap, ret = num; err_exit: - mtk_i2c_clock_disable(i2c); + clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); return ret; } @@ -1323,9 +1295,8 @@ static int mtk_i2c_probe(struct platform_device *pdev) { int ret = 0; struct mtk_i2c *i2c; - struct clk *clk; struct resource *res; - int irq; + int i, irq, speed_clk; i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); if (!i2c) @@ -1371,35 +1342,42 @@ static int mtk_i2c_probe(struct platform_device *pdev) if (i2c->have_pmic && !i2c->dev_comp->pmic_i2c) return -EINVAL; - i2c->clk_main = devm_clk_get(&pdev->dev, "main"); - if (IS_ERR(i2c->clk_main)) { + /* Fill in clk-bulk IDs */ + for (i = 0; i < I2C_MT65XX_CLK_MAX; i++) + i2c->clocks[i].id = i2c_mt65xx_clk_ids[i]; + + /* Get clocks one by one, some may be optional */ + i2c->clocks[I2C_MT65XX_CLK_MAIN].clk = devm_clk_get(&pdev->dev, "main"); + if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk)) { dev_err(&pdev->dev, "cannot get main clock\n"); - return PTR_ERR(i2c->clk_main); + return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_MAIN].clk); } - i2c->clk_dma = devm_clk_get(&pdev->dev, "dma"); - if (IS_ERR(i2c->clk_dma)) { + i2c->clocks[I2C_MT65XX_CLK_DMA].clk = devm_clk_get(&pdev->dev, "dma"); + if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk)) { dev_err(&pdev->dev, "cannot get dma clock\n"); - return PTR_ERR(i2c->clk_dma); + return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_DMA].clk); } - i2c->clk_arb = devm_clk_get(&pdev->dev, "arb"); - if (IS_ERR(i2c->clk_arb)) - i2c->clk_arb = NULL; + i2c->clocks[I2C_MT65XX_CLK_ARB].clk = devm_clk_get_optional(&pdev->dev, "arb"); + if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk)) + return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_ARB].clk); - clk = i2c->clk_main; if (i2c->have_pmic) { - i2c->clk_pmic = devm_clk_get(&pdev->dev, "pmic"); - if (IS_ERR(i2c->clk_pmic)) { + i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = devm_clk_get(&pdev->dev, "pmic"); + if (IS_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk)) { dev_err(&pdev->dev, "cannot get pmic clock\n"); - return PTR_ERR(i2c->clk_pmic); + return PTR_ERR(i2c->clocks[I2C_MT65XX_CLK_PMIC].clk); } - clk = i2c->clk_pmic; + speed_clk = I2C_MT65XX_CLK_PMIC; + } else { + i2c->clocks[I2C_MT65XX_CLK_PMIC].clk = NULL; + speed_clk = I2C_MT65XX_CLK_MAIN; } strlcpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); - ret = mtk_i2c_set_speed(i2c, clk_get_rate(clk)); + ret = mtk_i2c_set_speed(i2c, clk_get_rate(i2c->clocks[speed_clk].clk)); if (ret) { dev_err(&pdev->dev, "Failed to set the speed.\n"); return -EINVAL; @@ -1414,13 +1392,13 @@ static int mtk_i2c_probe(struct platform_device *pdev) } } - ret = mtk_i2c_clock_enable(i2c); + ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); if (ret) { dev_err(&pdev->dev, "clock enable failed!\n"); return ret; } mtk_i2c_init_hw(i2c); - mtk_i2c_clock_disable(i2c); + clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); ret = devm_request_irq(&pdev->dev, irq, mtk_i2c_irq, IRQF_NO_SUSPEND | IRQF_TRIGGER_NONE, @@ -1465,7 +1443,7 @@ static int mtk_i2c_resume_noirq(struct device *dev) int ret; struct mtk_i2c *i2c = dev_get_drvdata(dev); - ret = mtk_i2c_clock_enable(i2c); + ret = clk_bulk_prepare_enable(I2C_MT65XX_CLK_MAX, i2c->clocks); if (ret) { dev_err(dev, "clock enable failed!\n"); return ret; @@ -1473,7 +1451,7 @@ static int mtk_i2c_resume_noirq(struct device *dev) mtk_i2c_init_hw(i2c); - mtk_i2c_clock_disable(i2c); + clk_bulk_disable_unprepare(I2C_MT65XX_CLK_MAX, i2c->clocks); i2c_mark_adapter_resumed(&i2c->adap); -- cgit v1.2.3 From 1bff55b57ea16bac882715017fc9b8f9e16894e2 Mon Sep 17 00:00:00 2001 From: Kewei Xu Date: Mon, 7 Mar 2022 11:36:49 +0800 Subject: i2c: mediatek: Add i2c compatible for Mediatek MT8168 Add i2c compatible for MT8168. Compare to MT2712 i2c controller, MT8168 need to synchronize signal with dma. Signed-off-by: Kewei Xu Reviewed-by: AngeloGioacchino Del Regno Reviewed-by: Qii Wang Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-mt65xx.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index 7dde3b64766b..f651d3e124d6 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -389,6 +389,19 @@ static const struct mtk_i2c_compatible mt7622_compat = { .max_dma_support = 32, }; +static const struct mtk_i2c_compatible mt8168_compat = { + .regs = mt_i2c_regs_v1, + .pmic_i2c = 0, + .dcm = 1, + .auto_restart = 1, + .aux_len_reg = 1, + .timing_adjust = 1, + .dma_sync = 1, + .ltiming_adjust = 0, + .apdma_sync = 0, + .max_dma_support = 33, +}; + static const struct mtk_i2c_compatible mt8173_compat = { .regs = mt_i2c_regs_v1, .pmic_i2c = 0, @@ -448,6 +461,7 @@ static const struct of_device_id mtk_i2c_of_match[] = { { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, + { .compatible = "mediatek,mt8168-i2c", .data = &mt8168_compat }, { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, { .compatible = "mediatek,mt8186-i2c", .data = &mt8186_compat }, -- cgit v1.2.3 From d0583229bcf5090495d0621892d5da93d9fb6e3c Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 3 Mar 2022 12:17:13 -0700 Subject: i2c: designware: Mark dw_i2c_plat_{suspend,resume}() as __maybe_unused When CONFIG_PM is set but CONFIG_PM_SLEEP is not, two compiler warnings appear: drivers/i2c/busses/i2c-designware-platdrv.c:444:12: error: unused function 'dw_i2c_plat_suspend' [-Werror,-Wunused-function] static int dw_i2c_plat_suspend(struct device *dev) ^ drivers/i2c/busses/i2c-designware-platdrv.c:465:12: error: unused function 'dw_i2c_plat_resume' [-Werror,-Wunused-function] static int dw_i2c_plat_resume(struct device *dev) ^ 2 errors generated. These functions are only used in SET_LATE_SYSTEM_SLEEP_PM_OPS(), which is defined as empty when CONFIG_PM_SLEEP is not defined. Mark the functions as __maybe_unused to make it clear that these functions might be unused in this configuration. Fixes: c57813b8b288 ("i2c: designware: Lock the adapter while setting the suspended flag") Signed-off-by: Nathan Chancellor Reviewed-by: Hans de Goede Reviewed-by: Andy Shevchenko Acked-by: Jarkko Nikula Acked-by: Geert Uytterhoeven Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-platdrv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 116a297d1f6b..70ade5306e45 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -441,7 +441,7 @@ static int dw_i2c_plat_runtime_suspend(struct device *dev) return 0; } -static int dw_i2c_plat_suspend(struct device *dev) +static int __maybe_unused dw_i2c_plat_suspend(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); @@ -462,7 +462,7 @@ static int dw_i2c_plat_runtime_resume(struct device *dev) return 0; } -static int dw_i2c_plat_resume(struct device *dev) +static int __maybe_unused dw_i2c_plat_resume(struct device *dev) { struct dw_i2c_dev *i_dev = dev_get_drvdata(dev); -- cgit v1.2.3 From 8b6d8d00a46a12a4baa08d547d4231e50a558254 Mon Sep 17 00:00:00 2001 From: Xiang wangx Date: Mon, 6 Dec 2021 00:27:52 +0800 Subject: i2c: cros-ec-tunnel: Fix syntax errors in comments Delete the redundant word 'to' Signed-off-by: Xiang wangx [wsa: fixed prefix in subject] Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-cros-ec-tunnel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index 790ea3fda693..892213d51f43 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -107,7 +107,7 @@ static int ec_i2c_construct_message(u8 *buf, const struct i2c_msg i2c_msgs[], /** * ec_i2c_count_response - Count bytes needed for ec_i2c_parse_response * - * @i2c_msgs: The i2c messages to to fill up. + * @i2c_msgs: The i2c messages to fill up. * @num: The number of i2c messages expected. * * Returns the number of response bytes expeced. @@ -131,7 +131,7 @@ static int ec_i2c_count_response(struct i2c_msg i2c_msgs[], int num) * We'll take the EC's response and copy it back into msgs. * * @buf: The buffer to parse. - * @i2c_msgs: The i2c messages to to fill up. + * @i2c_msgs: The i2c messages to fill up. * @num: The number of i2c messages; will be modified to include the actual * number received. * -- cgit v1.2.3 From 3364c0ef8732694084f8238ffd9c62819209fd7f Mon Sep 17 00:00:00 2001 From: Jan Dabros Date: Mon, 14 Mar 2022 10:03:23 +0100 Subject: i2c: designware: Remove code duplication Simplify code by moving common part to one function. Signed-off-by: Jan Dabros Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Acked-by: Jarkko Nikula Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-designware-amdpsp.c | 35 +++++++++++++----------------- 1 file changed, 15 insertions(+), 20 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-designware-amdpsp.c b/drivers/i2c/busses/i2c-designware-amdpsp.c index 3844695e6bf3..9b37f2b95abc 100644 --- a/drivers/i2c/busses/i2c-designware-amdpsp.c +++ b/drivers/i2c/busses/i2c-designware-amdpsp.c @@ -214,17 +214,28 @@ static int psp_send_i2c_req(enum psp_i2c_req_type i2c_req_type) PSP_I2C_REQ_RETRY_DELAY_US, PSP_I2C_REQ_RETRY_CNT * PSP_I2C_REQ_RETRY_DELAY_US, 0, req); - if (ret) + if (ret) { + dev_err(psp_i2c_dev, "Timed out waiting for PSP to %s I2C bus\n", + (i2c_req_type == PSP_I2C_REQ_ACQUIRE) ? + "release" : "acquire"); goto cleanup; + } ret = status; - if (ret) + if (ret) { + dev_err(psp_i2c_dev, "PSP communication error\n"); goto cleanup; + } dev_dbg(psp_i2c_dev, "Request accepted by PSP after %ums\n", jiffies_to_msecs(jiffies - start)); cleanup: + if (ret) { + dev_err(psp_i2c_dev, "Assume i2c bus is for exclusive host usage\n"); + psp_i2c_mbox_fail = true; + } + kfree(req); return ret; } @@ -249,16 +260,8 @@ static int psp_acquire_i2c_bus(void) } status = psp_send_i2c_req(PSP_I2C_REQ_ACQUIRE); - if (status) { - if (status == -ETIMEDOUT) - dev_err(psp_i2c_dev, "Timed out waiting for PSP to release I2C bus\n"); - else - dev_err(psp_i2c_dev, "PSP communication error\n"); - - dev_err(psp_i2c_dev, "Assume i2c bus is for exclusive host usage\n"); - psp_i2c_mbox_fail = true; + if (status) goto cleanup; - } psp_i2c_sem_acquired = jiffies; psp_i2c_access_count++; @@ -294,16 +297,8 @@ static void psp_release_i2c_bus(void) /* Send a release command to PSP */ status = psp_send_i2c_req(PSP_I2C_REQ_RELEASE); - if (status) { - if (status == -ETIMEDOUT) - dev_err(psp_i2c_dev, "Timed out waiting for PSP to acquire I2C bus\n"); - else - dev_err(psp_i2c_dev, "PSP communication error\n"); - - dev_err(psp_i2c_dev, "Assume i2c bus is for exclusive host usage\n"); - psp_i2c_mbox_fail = true; + if (status) goto cleanup; - } dev_dbg(psp_i2c_dev, "PSP semaphore held for %ums\n", jiffies_to_msecs(jiffies - psp_i2c_sem_acquired)); -- cgit v1.2.3 From d714fb25e755ad96b699993fac47f48c4d6cebe9 Mon Sep 17 00:00:00 2001 From: Jae Hyun Yoo Date: Fri, 18 Mar 2022 13:41:33 -0700 Subject: i2c: add tracepoints for I2C slave events I2C slave events tracepoints can be enabled by: echo 1 > /sys/kernel/tracing/events/i2c_slave/enable and logs in /sys/kernel/tracing/trace will look like: ... i2c_slave: i2c-0 a=010 ret=0 WR_REQ [] ... i2c_slave: i2c-0 a=010 ret=0 WR_RCV [02] ... i2c_slave: i2c-0 a=010 ret=0 WR_RCV [0c] ... i2c_slave: i2c-0 a=010 ret=0 STOP [] ... i2c_slave: i2c-0 a=010 ret=0 RD_REQ [04] ... i2c_slave: i2c-0 a=010 ret=0 RD_PRO [b4] ... i2c_slave: i2c-0 a=010 ret=0 STOP [] formatted as: i2c- a= ret= <- callback return value [] trace printings can be selected by adding a filter like: echo adapter_nr==1 >/sys/kernel/tracing/events/i2c_slave/filter Signed-off-by: Jae Hyun Yoo Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-slave.c | 15 +++++++++ include/linux/i2c.h | 8 ++--- include/trace/events/i2c_slave.h | 67 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 include/trace/events/i2c_slave.h (limited to 'drivers/i2c') diff --git a/drivers/i2c/i2c-core-slave.c b/drivers/i2c/i2c-core-slave.c index 1589179d5eb9..e3765e12f93b 100644 --- a/drivers/i2c/i2c-core-slave.c +++ b/drivers/i2c/i2c-core-slave.c @@ -14,6 +14,9 @@ #include "i2c-core.h" +#define CREATE_TRACE_POINTS +#include + int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb) { int ret; @@ -79,6 +82,18 @@ int i2c_slave_unregister(struct i2c_client *client) } EXPORT_SYMBOL_GPL(i2c_slave_unregister); +int i2c_slave_event(struct i2c_client *client, + enum i2c_slave_event event, u8 *val) +{ + int ret = client->slave_cb(client, event, val); + + if (trace_i2c_slave_enabled()) + trace_i2c_slave(client, event, val, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(i2c_slave_event); + /** * i2c_detect_slave_mode - detect operation mode * @dev: The device owning the bus diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 7d4f52ceb7b5..fbda5ada2afc 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -392,12 +392,8 @@ enum i2c_slave_event { int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb); int i2c_slave_unregister(struct i2c_client *client); bool i2c_detect_slave_mode(struct device *dev); - -static inline int i2c_slave_event(struct i2c_client *client, - enum i2c_slave_event event, u8 *val) -{ - return client->slave_cb(client, event, val); -} +int i2c_slave_event(struct i2c_client *client, + enum i2c_slave_event event, u8 *val); #else static inline bool i2c_detect_slave_mode(struct device *dev) { return false; } #endif diff --git a/include/trace/events/i2c_slave.h b/include/trace/events/i2c_slave.h new file mode 100644 index 000000000000..811166abbe3a --- /dev/null +++ b/include/trace/events/i2c_slave.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * I2C slave tracepoints + * + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM i2c_slave + +#if !defined(_TRACE_I2C_SLAVE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_I2C_SLAVE_H + +#include +#include + +TRACE_DEFINE_ENUM(I2C_SLAVE_READ_REQUESTED); +TRACE_DEFINE_ENUM(I2C_SLAVE_WRITE_REQUESTED); +TRACE_DEFINE_ENUM(I2C_SLAVE_READ_PROCESSED); +TRACE_DEFINE_ENUM(I2C_SLAVE_WRITE_RECEIVED); +TRACE_DEFINE_ENUM(I2C_SLAVE_STOP); + +#define show_event_type(type) \ + __print_symbolic(type, \ + { I2C_SLAVE_READ_REQUESTED, "RD_REQ" }, \ + { I2C_SLAVE_WRITE_REQUESTED, "WR_REQ" }, \ + { I2C_SLAVE_READ_PROCESSED, "RD_PRO" }, \ + { I2C_SLAVE_WRITE_RECEIVED, "WR_RCV" }, \ + { I2C_SLAVE_STOP, " STOP" }) + +TRACE_EVENT(i2c_slave, + TP_PROTO(const struct i2c_client *client, enum i2c_slave_event event, + __u8 *val, int cb_ret), + TP_ARGS(client, event, val, cb_ret), + TP_STRUCT__entry( + __field(int, adapter_nr ) + __field(int, ret ) + __field(__u16, addr ) + __field(__u16, len ) + __field(enum i2c_slave_event, event ) + __array(__u8, buf, 1) ), + + TP_fast_assign( + __entry->adapter_nr = client->adapter->nr; + __entry->addr = client->addr; + __entry->event = event; + __entry->ret = cb_ret; + switch (event) { + case I2C_SLAVE_READ_REQUESTED: + case I2C_SLAVE_READ_PROCESSED: + case I2C_SLAVE_WRITE_RECEIVED: + __entry->len = 1; + memcpy(__entry->buf, val, __entry->len); + break; + default: + __entry->len = 0; + break; + } + ), + TP_printk("i2c-%d a=%03x ret=%d %s [%*phD]", + __entry->adapter_nr, __entry->addr, __entry->ret, + show_event_type(__entry->event), __entry->len, __entry->buf + )); + +#endif /* _TRACE_I2C_SLAVE_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From cb13aa16f34f794a9cee2626862af8a95f0f0ee9 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Tue, 1 Mar 2022 07:26:00 +0000 Subject: i2c: meson: Fix wrong speed use from probe Having meson_i2c_set_clk_div after i2c_add_adapter causes issues for client drivers that try to use the bus before the requested speed is applied. The bus can be used just after i2c_add_adapter, so move i2c_add_adapter to the final step as meson_i2c_set_clk_div needs to be called before the bus is used. Fixes: 09af1c2fa490 ("i2c: meson: set clock divider in probe instead of setting it for each transfer") Signed-off-by: Lucas Tanure Reviewed-by: Neil Armstrong Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-meson.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c index ef73a42577cc..07eb819072c4 100644 --- a/drivers/i2c/busses/i2c-meson.c +++ b/drivers/i2c/busses/i2c-meson.c @@ -465,18 +465,18 @@ static int meson_i2c_probe(struct platform_device *pdev) */ meson_i2c_set_mask(i2c, REG_CTRL, REG_CTRL_START, 0); - ret = i2c_add_adapter(&i2c->adap); - if (ret < 0) { - clk_disable_unprepare(i2c->clk); - return ret; - } - /* Disable filtering */ meson_i2c_set_mask(i2c, REG_SLAVE_ADDR, REG_SLV_SDA_FILTER | REG_SLV_SCL_FILTER, 0); meson_i2c_set_clk_div(i2c, timings.bus_freq_hz); + ret = i2c_add_adapter(&i2c->adap); + if (ret < 0) { + clk_disable_unprepare(i2c->clk); + return ret; + } + return 0; } -- cgit v1.2.3 From 1a22aabf20adf89cb216f566913196128766f25b Mon Sep 17 00:00:00 2001 From: Peter Rosin Date: Wed, 2 Mar 2022 12:22:35 +0100 Subject: i2c: mux: demux-pinctrl: do not deactivate a master that is not active Attempting to rollback the activation of the current master when the current master has not been activated is bad. priv->cur_chan and priv->cur_adap are both still zeroed out and the rollback may result in attempts to revert an of changeset that has not been applied and do result in calls to both del and put the zeroed out i2c_adapter. Maybe it crashes, or whatever, but it's bad in any case. Fixes: e9d1a0a41d44 ("i2c: mux: demux-pinctrl: Fix an error handling path in 'i2c_demux_pinctrl_probe()'") Signed-off-by: Peter Rosin Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-demux-pinctrl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/i2c') diff --git a/drivers/i2c/muxes/i2c-demux-pinctrl.c b/drivers/i2c/muxes/i2c-demux-pinctrl.c index 5365199a31f4..f7a7405d4350 100644 --- a/drivers/i2c/muxes/i2c-demux-pinctrl.c +++ b/drivers/i2c/muxes/i2c-demux-pinctrl.c @@ -261,7 +261,7 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev) err = device_create_file(&pdev->dev, &dev_attr_available_masters); if (err) - goto err_rollback; + goto err_rollback_activation; err = device_create_file(&pdev->dev, &dev_attr_current_master); if (err) @@ -271,8 +271,9 @@ static int i2c_demux_pinctrl_probe(struct platform_device *pdev) err_rollback_available: device_remove_file(&pdev->dev, &dev_attr_available_masters); -err_rollback: +err_rollback_activation: i2c_demux_deactivate_master(priv); +err_rollback: for (j = 0; j < i; j++) { of_node_put(priv->chan[j].parent_np); of_changeset_destroy(&priv->chan[j].chgset); -- cgit v1.2.3