From c6250d0eab82da7af78e8d010360c91f33cbc242 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 13:45:04 +0200 Subject: power: ip5xxx_power: Make use of i2c_get_match_data() Get matching data in one step by switching to use i2c_get_match_data(). Acked-by: Sebastian Reichel Signed-off-by: Andy Shevchenko Reviewed-by: Vladimir Zapolskiy Signed-off-by: Wolfram Sang --- drivers/power/supply/ip5xxx_power.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/power/supply/ip5xxx_power.c b/drivers/power/supply/ip5xxx_power.c index c448e0ac0dfa..a293b2765771 100644 --- a/drivers/power/supply/ip5xxx_power.c +++ b/drivers/power/supply/ip5xxx_power.c @@ -828,10 +828,9 @@ static void ip5xxx_setup_regs(struct device *dev, struct ip5xxx *ip5xxx, static int ip5xxx_power_probe(struct i2c_client *client) { - const struct ip5xxx_regfield_config *fields = &ip51xx_fields; + const struct ip5xxx_regfield_config *fields; struct power_supply_config psy_cfg = {}; struct device *dev = &client->dev; - const struct of_device_id *of_id; struct power_supply *psy; struct ip5xxx *ip5xxx; @@ -843,9 +842,7 @@ static int ip5xxx_power_probe(struct i2c_client *client) if (IS_ERR(ip5xxx->regmap)) return PTR_ERR(ip5xxx->regmap); - of_id = i2c_of_match_device(dev->driver->of_match_table, client); - if (of_id) - fields = (const struct ip5xxx_regfield_config *)of_id->data; + fields = i2c_get_match_data(client) ?: &ip51xx_fields; ip5xxx_setup_regs(dev, ip5xxx, fields); psy_cfg.of_node = dev->of_node; -- cgit v1.2.3-59-g8ed1b From fbc54ae4f8d7cef3b11f70945bf8df8f952814b6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 13:45:05 +0200 Subject: i2c: Unexport i2c_of_match_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit i2c_of_match_device() is not used anymore outside of I²C framework, unexport it. Signed-off-by: Andy Shevchenko Reviewed-by: Vladimir Zapolskiy Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-of.c | 1 - drivers/i2c/i2c-core.h | 9 +++++++++ include/linux/i2c.h | 11 ----------- 3 files changed, 9 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-core-of.c b/drivers/i2c/i2c-core-of.c index a6c407d36800..02feee6c9ba9 100644 --- a/drivers/i2c/i2c-core-of.c +++ b/drivers/i2c/i2c-core-of.c @@ -157,7 +157,6 @@ const struct of_device_id return i2c_of_match_device_sysfs(matches, client); } -EXPORT_SYMBOL_GPL(i2c_of_match_device); #if IS_ENABLED(CONFIG_OF_DYNAMIC) static int of_i2c_notify(struct notifier_block *nb, unsigned long action, diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h index 36587f38dff3..4797ba88331c 100644 --- a/drivers/i2c/i2c-core.h +++ b/drivers/i2c/i2c-core.h @@ -84,8 +84,17 @@ static inline void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) { #ifdef CONFIG_OF void of_i2c_register_devices(struct i2c_adapter *adap); +const struct of_device_id *i2c_of_match_device(const struct of_device_id *matches, + struct i2c_client *client); + #else static inline void of_i2c_register_devices(struct i2c_adapter *adap) { } +static inline +const struct of_device_id *i2c_of_match_device(const struct of_device_id *matches, + struct i2c_client *client) +{ + return NULL; +} #endif extern struct notifier_block i2c_of_notifier; diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 2b2af24d2a43..997e80649889 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -1029,10 +1029,6 @@ static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node return i2c_get_adapter_by_fwnode(of_fwnode_handle(node)); } -const struct of_device_id -*i2c_of_match_device(const struct of_device_id *matches, - struct i2c_client *client); - int of_i2c_get_board_info(struct device *dev, struct device_node *node, struct i2c_board_info *info); @@ -1053,13 +1049,6 @@ static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node return NULL; } -static inline const struct of_device_id -*i2c_of_match_device(const struct of_device_id *matches, - struct i2c_client *client) -{ - return NULL; -} - static inline int of_i2c_get_board_info(struct device *dev, struct device_node *node, struct i2c_board_info *info) -- cgit v1.2.3-59-g8ed1b From f25f405d250fdc7e2e67be8d1732cbbc7cd782d0 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 25 Feb 2025 12:08:38 +0200 Subject: eeprom: at24: Drop of_match_ptr() and ACPI_PTR() protections These result in a very small reduction in driver size, but at the cost of more complex build and slightly harder to read code. In the case of of_match_ptr() it also prevents use of PRP0001 ACPI based identification. In this particular case we have a valid ACPI/PNP ID that should be used in preference to PRP0001 but doesn't mean we should prevent that route. With this done, drop unneeded of*.h inclusions and __maybe_unused markers. Signed-off-by: Andy Shevchenko Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20250225100838.362125-1-andriy.shevchenko@linux.intel.com Signed-off-by: Bartosz Golaszewski --- drivers/misc/eeprom/at24.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 0a7c7f29406c..f721825199ce 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include #include @@ -252,7 +250,7 @@ static const struct i2c_device_id at24_ids[] = { }; MODULE_DEVICE_TABLE(i2c, at24_ids); -static const struct of_device_id __maybe_unused at24_of_match[] = { +static const struct of_device_id at24_of_match[] = { { .compatible = "atmel,24c00", .data = &at24_data_24c00 }, { .compatible = "atmel,24c01", .data = &at24_data_24c01 }, { .compatible = "atmel,24cs01", .data = &at24_data_24cs01 }, @@ -286,7 +284,7 @@ static const struct of_device_id __maybe_unused at24_of_match[] = { }; MODULE_DEVICE_TABLE(of, at24_of_match); -static const struct acpi_device_id __maybe_unused at24_acpi_ids[] = { +static const struct acpi_device_id at24_acpi_ids[] = { { "INT3499", (kernel_ulong_t)&at24_data_INT3499 }, { "TPF0001", (kernel_ulong_t)&at24_data_24c1024 }, { /* END OF LIST */ } @@ -848,8 +846,8 @@ static struct i2c_driver at24_driver = { .driver = { .name = "at24", .pm = &at24_pm_ops, - .of_match_table = of_match_ptr(at24_of_match), - .acpi_match_table = ACPI_PTR(at24_acpi_ids), + .of_match_table = at24_of_match, + .acpi_match_table = at24_acpi_ids, }, .probe = at24_probe, .remove = at24_remove, -- cgit v1.2.3-59-g8ed1b From 517ec053eeb48f4fe96271b32b3df9c8e269a29c Mon Sep 17 00:00:00 2001 From: Aryan Srivastava Date: Thu, 10 Oct 2024 15:53:15 +1300 Subject: i2c: octeon: refactor common i2c operations Refactor the current implementation of the high-level composite read and write operations in preparation of the addition of block-mode read/write operations. The sending of the i2c command is generic and will apply for both the block-mode and non-block-mode ops. Extract this from the current hlc ops, and place into a generic function, octeon_i2c_hlc_cmd_send. The considerations made for extended addresses in the command construction are almost common for all cases, extract these into octeon_i2c_hlc_ext. There is one difference between the extended read and write cases. When performing extended read or writes the SW_TWSI_EXT must be written with an extended internal address, but the data field is only filled in the write case (read back in read case). This results in the original code block for the read case immediately writing this register, while the write case fills in any data and then writes the register. To create a common block of code for both processes remove the SW_TWSI_EXT write from within the code block and instead in it's place a variable is set, set_ext, which is returned and used as a condition to do the register write, in the read command case. There are parts of the commands construction which are common (only in the read case), extract this and place into generic function octeon_i2c_hlc_read_cmd. This function also reads the return from octeon_i2c_hlc_ext and completes the write to SW_TWSI_EXT if required. The write commands cannot be made entirely into common code as there are distinct differences in the block mode and non-block-mode process. Particularly the writing of data into the buffer. Signed-off-by: Aryan Srivastava Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20241010025317.2040470-2-aryan.srivastava@alliedtelesis.co.nz --- drivers/i2c/busses/i2c-octeon-core.c | 86 ++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c index 16cc34a0526e..3fbc828508ab 100644 --- a/drivers/i2c/busses/i2c-octeon-core.c +++ b/drivers/i2c/busses/i2c-octeon-core.c @@ -498,6 +498,50 @@ err: return ret; } +/* Process hlc transaction */ +static int octeon_i2c_hlc_cmd_send(struct octeon_i2c *i2c, u64 cmd) +{ + octeon_i2c_hlc_int_clear(i2c); + octeon_i2c_writeq_flush(cmd, i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)); + + return octeon_i2c_hlc_wait(i2c); +} + +/* Generic consideration for extended internal addresses in i2c hlc r/w ops */ +static bool octeon_i2c_hlc_ext(struct octeon_i2c *i2c, struct i2c_msg msg, u64 *cmd_in, u64 *ext) +{ + bool set_ext = false; + u64 cmd = 0; + + if (msg.flags & I2C_M_TEN) + cmd |= SW_TWSI_OP_10_IA; + else + cmd |= SW_TWSI_OP_7_IA; + + if (msg.len == 2) { + cmd |= SW_TWSI_EIA; + *ext = (u64)msg.buf[0] << SW_TWSI_IA_SHIFT; + cmd |= (u64)msg.buf[1] << SW_TWSI_IA_SHIFT; + set_ext = true; + } else { + cmd |= (u64)msg.buf[0] << SW_TWSI_IA_SHIFT; + } + + *cmd_in |= cmd; + return set_ext; +} + +/* Construct and send i2c transaction core cmd for read ops */ +static int octeon_i2c_hlc_read_cmd(struct octeon_i2c *i2c, struct i2c_msg msg, u64 cmd) +{ + u64 ext = 0; + + if (octeon_i2c_hlc_ext(i2c, msg, &cmd, &ext)) + octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c)); + + return octeon_i2c_hlc_cmd_send(i2c, cmd); +} + /* high-level-controller composite write+read, msg0=addr, msg1=data */ static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) { @@ -512,26 +556,8 @@ static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs /* A */ cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; - if (msgs[0].flags & I2C_M_TEN) - cmd |= SW_TWSI_OP_10_IA; - else - cmd |= SW_TWSI_OP_7_IA; - - if (msgs[0].len == 2) { - u64 ext = 0; - - cmd |= SW_TWSI_EIA; - ext = (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; - cmd |= (u64)msgs[0].buf[1] << SW_TWSI_IA_SHIFT; - octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c)); - } else { - cmd |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; - } - - octeon_i2c_hlc_int_clear(i2c); - octeon_i2c_writeq_flush(cmd, i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)); - - ret = octeon_i2c_hlc_wait(i2c); + /* Send core command */ + ret = octeon_i2c_hlc_read_cmd(i2c, msgs[0], cmd); if (ret) goto err; @@ -567,19 +593,8 @@ static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msg /* A */ cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; - if (msgs[0].flags & I2C_M_TEN) - cmd |= SW_TWSI_OP_10_IA; - else - cmd |= SW_TWSI_OP_7_IA; - - if (msgs[0].len == 2) { - cmd |= SW_TWSI_EIA; - ext |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; - set_ext = true; - cmd |= (u64)msgs[0].buf[1] << SW_TWSI_IA_SHIFT; - } else { - cmd |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; - } + /* Set parameters for extended message (if required) */ + set_ext = octeon_i2c_hlc_ext(i2c, msgs[0], &cmd, &ext); for (i = 0, j = msgs[1].len - 1; i < msgs[1].len && i < 4; i++, j--) cmd |= (u64)msgs[1].buf[j] << (8 * i); @@ -592,10 +607,7 @@ static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msg if (set_ext) octeon_i2c_writeq_flush(ext, i2c->twsi_base + OCTEON_REG_SW_TWSI_EXT(i2c)); - octeon_i2c_hlc_int_clear(i2c); - octeon_i2c_writeq_flush(cmd, i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)); - - ret = octeon_i2c_hlc_wait(i2c); + ret = octeon_i2c_hlc_cmd_send(i2c, cmd); if (ret) goto err; -- cgit v1.2.3-59-g8ed1b From be7113d2e2a6f20cbee99c98d261a1fd6fd7b549 Mon Sep 17 00:00:00 2001 From: Vitalii Mordan Date: Wed, 12 Feb 2025 20:28:03 +0300 Subject: i2c: pxa: fix call balance of i2c->clk handling routines If the clock i2c->clk was not enabled in i2c_pxa_probe(), it should not be disabled in any path. Found by Linux Verification Center (linuxtesting.org) with Klever. Signed-off-by: Vitalii Mordan Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250212172803.1422136-1-mordan@ispras.ru --- drivers/i2c/busses/i2c-pxa.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index cb6988482673..4415a29f749b 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -1503,7 +1503,10 @@ static int i2c_pxa_probe(struct platform_device *dev) i2c->adap.name); } - clk_prepare_enable(i2c->clk); + ret = clk_prepare_enable(i2c->clk); + if (ret) + return dev_err_probe(&dev->dev, ret, + "failed to enable clock\n"); if (i2c->use_pio) { i2c->adap.algo = &i2c_pxa_pio_algorithm; -- cgit v1.2.3-59-g8ed1b From 8f95d1da03e9cc3797038538369518ad685a7748 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 17 Feb 2025 18:17:08 +0530 Subject: i2c: amd: Switch to guard(mutex) Instead of using the 'goto label; mutex_unlock()' pattern use 'guard(mutex)' which will release the mutex when it goes out of scope. Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Reviewed-by: Andy Shevchenko Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250217124709.3121848-1-Shyam-sundar.S-k@amd.com --- drivers/i2c/busses/i2c-designware-amdpsp.c | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-designware-amdpsp.c b/drivers/i2c/busses/i2c-designware-amdpsp.c index 8fbd2a10c31a..404571ad61a8 100644 --- a/drivers/i2c/busses/i2c-designware-amdpsp.c +++ b/drivers/i2c/busses/i2c-designware-amdpsp.c @@ -151,19 +151,16 @@ static void release_bus(void) static void psp_release_i2c_bus_deferred(struct work_struct *work) { - mutex_lock(&psp_i2c_access_mutex); + guard(mutex)(&psp_i2c_access_mutex); /* * If there is any pending transaction, cannot release the bus here. * psp_release_i2c_bus() will take care of this later. */ if (psp_i2c_access_count) - goto cleanup; + return; release_bus(); - -cleanup: - mutex_unlock(&psp_i2c_access_mutex); } static DECLARE_DELAYED_WORK(release_queue, psp_release_i2c_bus_deferred); @@ -171,11 +168,11 @@ static int psp_acquire_i2c_bus(void) { int status; - mutex_lock(&psp_i2c_access_mutex); + guard(mutex)(&psp_i2c_access_mutex); /* Return early if mailbox malfunctioned */ if (psp_i2c_mbox_fail) - goto cleanup; + return 0; psp_i2c_access_count++; @@ -184,11 +181,11 @@ static int psp_acquire_i2c_bus(void) * reservation period. */ if (psp_i2c_sem_acquired) - goto cleanup; + return 0; status = psp_send_i2c_req(PSP_I2C_REQ_ACQUIRE); if (status) - goto cleanup; + return 0; psp_i2c_sem_acquired = jiffies; @@ -201,18 +198,16 @@ static int psp_acquire_i2c_bus(void) * 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) { - mutex_lock(&psp_i2c_access_mutex); + guard(mutex)(&psp_i2c_access_mutex); /* Return early if mailbox was malfunctioned */ if (psp_i2c_mbox_fail) - goto cleanup; + return; /* * If we are last owner of PSP semaphore, need to release arbitration @@ -220,7 +215,7 @@ static void psp_release_i2c_bus(void) */ psp_i2c_access_count--; if (psp_i2c_access_count) - goto cleanup; + return; /* * Send a release command to PSP if the semaphore reservation timeout @@ -228,9 +223,6 @@ static void psp_release_i2c_bus(void) */ if (!delayed_work_pending(&release_queue)) release_bus(); - -cleanup: - mutex_unlock(&psp_i2c_access_mutex); } /* -- cgit v1.2.3-59-g8ed1b From a71248d96662da5f5d0e276776d1679864ad1bf2 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 17 Feb 2025 18:17:09 +0530 Subject: i2c: dw: Update the master_xfer callback name In light of the recent updates to the i2c subsystem, ensure to use the correct callback names. Specifically, replace '.master_xfer' with '.xfer'. Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Reviewed-by: Andy Shevchenko Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250217124709.3121848-2-Shyam-sundar.S-k@amd.com --- drivers/i2c/busses/i2c-designware-master.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index 2569bf1a72e0..c5394229b77f 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -907,7 +907,7 @@ done_nolock: } static const struct i2c_algorithm i2c_dw_algo = { - .master_xfer = i2c_dw_xfer, + .xfer = i2c_dw_xfer, .functionality = i2c_dw_func, }; -- cgit v1.2.3-59-g8ed1b From 1505986abf18c40003ebc6d2357454e05b927a7e Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 17 Feb 2025 14:32:56 +0530 Subject: i2c: amd-asf: Modify callbacks of i2c_algorithm to align with the latest revision Adjust the i2c_algorithm callbacks to be consistent with the most recent revision by updating the callback names from master_xfer, reg_slave, and unreg_slave to the current naming convention: xfer, reg_target, and unreg_target. Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250217090258.398540-1-Shyam-sundar.S-k@amd.com --- drivers/i2c/busses/i2c-amd-asf-plat.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-amd-asf-plat.c b/drivers/i2c/busses/i2c-amd-asf-plat.c index 93ebec162c6d..ca0fb46b73bd 100644 --- a/drivers/i2c/busses/i2c-amd-asf-plat.c +++ b/drivers/i2c/busses/i2c-amd-asf-plat.c @@ -272,9 +272,9 @@ static u32 amd_asf_func(struct i2c_adapter *adapter) } static const struct i2c_algorithm amd_asf_smbus_algorithm = { - .master_xfer = amd_asf_xfer, - .reg_slave = amd_asf_reg_target, - .unreg_slave = amd_asf_unreg_target, + .xfer = amd_asf_xfer, + .reg_target = amd_asf_reg_target, + .unreg_target = amd_asf_unreg_target, .functionality = amd_asf_func, }; -- cgit v1.2.3-59-g8ed1b From b719afaa1e5d88a1b51d76adf344ff4a48efdb45 Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Mon, 17 Feb 2025 14:32:57 +0530 Subject: i2c: amd-asf: Set cmd variable when encountering an error In the event of ASF error during the transfer, update the cmd and exit the process, as data processing is not performed when a command fails. Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250217090258.398540-2-Shyam-sundar.S-k@amd.com --- drivers/i2c/busses/i2c-amd-asf-plat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-amd-asf-plat.c b/drivers/i2c/busses/i2c-amd-asf-plat.c index ca0fb46b73bd..ca45f0f23321 100644 --- a/drivers/i2c/busses/i2c-amd-asf-plat.c +++ b/drivers/i2c/busses/i2c-amd-asf-plat.c @@ -69,7 +69,7 @@ static void amd_asf_process_target(struct work_struct *work) /* Check if no error bits are set in target status register */ if (reg & ASF_ERROR_STATUS) { /* Set bank as full */ - cmd = 0; + cmd = 1; reg |= GENMASK(3, 2); outb_p(reg, ASFDATABNKSEL); } else { -- cgit v1.2.3-59-g8ed1b From 1a64b21282ddc4f7ec8aeb8195c20cf15bd32525 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 25 Feb 2025 17:36:56 +0100 Subject: i2c: mux: remove incorrect of_match_ptr annotations Building with W=1 shows a warning about ltc4306_of_match and i2c_mux_reg_of_match being unused when CONFIG_OF is disabled: drivers/i2c/muxes/i2c-mux-ltc4306.c:200:34: error: unused variable 'ltc4306_of_match' [-Werror,-Wunused-const-variable] drivers/i2c/muxes/i2c-mux-reg.c:242:34: error: unused variable 'i2c_mux_reg_of_match' [-Werror,-Wunused-const-variable] Acked-by: Peter Rosin Signed-off-by: Arnd Bergmann Acked-by: Michael Hennerich Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250225163700.4169480-1-arnd@kernel.org --- drivers/i2c/muxes/i2c-mux-ltc4306.c | 2 +- drivers/i2c/muxes/i2c-mux-reg.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/muxes/i2c-mux-ltc4306.c b/drivers/i2c/muxes/i2c-mux-ltc4306.c index 19a7c370946d..8a87f19bf5d5 100644 --- a/drivers/i2c/muxes/i2c-mux-ltc4306.c +++ b/drivers/i2c/muxes/i2c-mux-ltc4306.c @@ -303,7 +303,7 @@ static void ltc4306_remove(struct i2c_client *client) static struct i2c_driver ltc4306_driver = { .driver = { .name = "ltc4306", - .of_match_table = of_match_ptr(ltc4306_of_match), + .of_match_table = ltc4306_of_match, }, .probe = ltc4306_probe, .remove = ltc4306_remove, diff --git a/drivers/i2c/muxes/i2c-mux-reg.c b/drivers/i2c/muxes/i2c-mux-reg.c index dfa472d514cc..1e566ea92bc9 100644 --- a/drivers/i2c/muxes/i2c-mux-reg.c +++ b/drivers/i2c/muxes/i2c-mux-reg.c @@ -250,7 +250,7 @@ static struct platform_driver i2c_mux_reg_driver = { .remove = i2c_mux_reg_remove, .driver = { .name = "i2c-mux-reg", - .of_match_table = of_match_ptr(i2c_mux_reg_of_match), + .of_match_table = i2c_mux_reg_of_match, }, }; -- cgit v1.2.3-59-g8ed1b From d4f35233a6345f62637463ef6e0708f44ffaa583 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Tue, 28 Nov 2023 10:48:37 +0100 Subject: i2c: qup: Vote for interconnect bandwidth to DRAM When the I2C QUP controller is used together with a DMA engine it needs to vote for the interconnect path to the DRAM. Otherwise it may be unable to access the memory quickly enough. The requested peak bandwidth is dependent on the I2C core clock. To avoid sending votes too often the bandwidth is always requested when a DMA transfer starts, but dropped only on runtime suspend. Runtime suspend should only happen if no transfer is active. After resumption we can defer the next vote until the first DMA transfer actually happens. The implementation is largely identical to the one introduced for spi-qup in commit ecdaa9473019 ("spi: qup: Vote for interconnect bandwidth to DRAM") since both drivers represent the same hardware block. Signed-off-by: Stephan Gerhold Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20231128-i2c-qup-dvfs-v1-3-59a0e3039111@kernkonzept.com --- drivers/i2c/busses/i2c-qup.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c index da20b4487c9a..3a36d682ed57 100644 --- a/drivers/i2c/busses/i2c-qup.c +++ b/drivers/i2c/busses/i2c-qup.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -150,6 +151,8 @@ /* TAG length for DATA READ in RX FIFO */ #define READ_RX_TAGS_LEN 2 +#define QUP_BUS_WIDTH 8 + static unsigned int scl_freq; module_param_named(scl_freq, scl_freq, uint, 0444); MODULE_PARM_DESC(scl_freq, "SCL frequency override"); @@ -227,6 +230,7 @@ struct qup_i2c_dev { int irq; struct clk *clk; struct clk *pclk; + struct icc_path *icc_path; struct i2c_adapter adap; int clk_ctl; @@ -255,6 +259,10 @@ struct qup_i2c_dev { /* To configure when bus is in run state */ u32 config_run; + /* bandwidth votes */ + u32 src_clk_freq; + u32 cur_bw_clk_freq; + /* dma parameters */ bool is_dma; /* To check if the current transfer is using DMA */ @@ -453,6 +461,23 @@ static int qup_i2c_bus_active(struct qup_i2c_dev *qup, int len) return ret; } +static int qup_i2c_vote_bw(struct qup_i2c_dev *qup, u32 clk_freq) +{ + u32 needed_peak_bw; + int ret; + + if (qup->cur_bw_clk_freq == clk_freq) + return 0; + + needed_peak_bw = Bps_to_icc(clk_freq * QUP_BUS_WIDTH); + ret = icc_set_bw(qup->icc_path, 0, needed_peak_bw); + if (ret) + return ret; + + qup->cur_bw_clk_freq = clk_freq; + return 0; +} + static void qup_i2c_write_tx_fifo_v1(struct qup_i2c_dev *qup) { struct qup_i2c_block *blk = &qup->blk; @@ -838,6 +863,10 @@ static int qup_i2c_bam_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, int ret = 0; int idx = 0; + ret = qup_i2c_vote_bw(qup, qup->src_clk_freq); + if (ret) + return ret; + enable_irq(qup->irq); ret = qup_i2c_req_dma(qup); @@ -1643,6 +1672,7 @@ static void qup_i2c_disable_clocks(struct qup_i2c_dev *qup) config = readl(qup->base + QUP_CONFIG); config |= QUP_CLOCK_AUTO_GATE; writel(config, qup->base + QUP_CONFIG); + qup_i2c_vote_bw(qup, 0); clk_disable_unprepare(qup->pclk); } @@ -1743,6 +1773,11 @@ static int qup_i2c_probe(struct platform_device *pdev) goto fail_dma; } qup->is_dma = true; + + qup->icc_path = devm_of_icc_get(&pdev->dev, NULL); + if (IS_ERR(qup->icc_path)) + return dev_err_probe(&pdev->dev, PTR_ERR(qup->icc_path), + "failed to get interconnect path\n"); } nodma: @@ -1791,6 +1826,7 @@ nodma: qup_i2c_enable_clocks(qup); src_clk_freq = clk_get_rate(qup->clk); } + qup->src_clk_freq = src_clk_freq; /* * Bootloaders might leave a pending interrupt on certain QUP's, -- cgit v1.2.3-59-g8ed1b From 6af58d3ec736173a677ba56e579f9b01e38ef2f9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 16:07:16 +0200 Subject: i2c: axxia: Use i2c_10bit_addr_*_from_msg() helpers Use i2c_10bit_addr_*_from_msg() helpers instead of local copy. No functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213141045.2716943-3-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-axxia.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c index 48916cf45ff7..50030256cd85 100644 --- a/drivers/i2c/busses/i2c-axxia.c +++ b/drivers/i2c/busses/i2c-axxia.c @@ -255,11 +255,6 @@ static int i2c_m_rd(const struct i2c_msg *msg) return (msg->flags & I2C_M_RD) != 0; } -static int i2c_m_ten(const struct i2c_msg *msg) -{ - return (msg->flags & I2C_M_TEN) != 0; -} - static int i2c_m_recv_len(const struct i2c_msg *msg) { return (msg->flags & I2C_M_RECV_LEN) != 0; @@ -439,20 +434,10 @@ static void axxia_i2c_set_addr(struct axxia_i2c_dev *idev, struct i2c_msg *msg) { u32 addr_1, addr_2; - if (i2c_m_ten(msg)) { - /* 10-bit address - * addr_1: 5'b11110 | addr[9:8] | (R/nW) - * addr_2: addr[7:0] - */ - addr_1 = 0xF0 | ((msg->addr >> 7) & 0x06); - if (i2c_m_rd(msg)) - addr_1 |= 1; /* Set the R/nW bit of the address */ - addr_2 = msg->addr & 0xFF; + if (msg->flags & I2C_M_TEN) { + addr_1 = i2c_10bit_addr_hi_from_msg(msg); + addr_2 = i2c_10bit_addr_lo_from_msg(msg); } else { - /* 7-bit address - * addr_1: addr[6:0] | (R/nW) - * addr_2: dont care - */ addr_1 = i2c_8bit_addr_from_msg(msg); addr_2 = 0; } -- cgit v1.2.3-59-g8ed1b From eaa0df0de963852ffa19dc6ddb765b9a9e169b63 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 16:07:17 +0200 Subject: i2c: bcm-kona: Use i2c_10bit_addr_*_from_msg() helpers Use i2c_10bit_addr_*_from_msg() helpers instead of local copy. No functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213141045.2716943-4-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-bcm-kona.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c index 340fe1305dd9..9d8838bbd938 100644 --- a/drivers/i2c/busses/i2c-bcm-kona.c +++ b/drivers/i2c/busses/i2c-bcm-kona.c @@ -471,12 +471,12 @@ static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev, if (msg->flags & I2C_M_TEN) { /* First byte is 11110XX0 where XX is upper 2 bits */ - addr = 0xF0 | ((msg->addr & 0x300) >> 7); + addr = i2c_10bit_addr_hi_from_msg(msg) & ~I2C_M_RD; if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) return -EREMOTEIO; /* Second byte is the remaining 8 bits */ - addr = msg->addr & 0xFF; + addr = i2c_10bit_addr_lo_from_msg(msg); if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) return -EREMOTEIO; @@ -486,7 +486,7 @@ static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev, return -EREMOTEIO; /* Then re-send the first byte with the read bit set */ - addr = 0xF0 | ((msg->addr & 0x300) >> 7) | 0x01; + addr = i2c_10bit_addr_hi_from_msg(msg); if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) return -EREMOTEIO; } -- cgit v1.2.3-59-g8ed1b From 3bf45fb5707818cff81997dd03bfbd9c1b3428f8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 16:07:18 +0200 Subject: i2c: brcmstb: Use i2c_10bit_addr_*_from_msg() helpers Use i2c_10bit_addr_*_from_msg() helpers instead of local copy. No functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213141045.2716943-5-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-brcmstb.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index 00f1a046e985..5fa30e8926c5 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -414,23 +414,22 @@ static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev, if (msg->flags & I2C_M_TEN) { /* First byte is 11110XX0 where XX is upper 2 bits */ - addr = 0xF0 | ((msg->addr & 0x300) >> 7); + addr = i2c_10bit_addr_hi_from_msg(msg) & ~I2C_M_RD; bsc_writel(dev, addr, chip_address); /* Second byte is the remaining 8 bits */ - addr = msg->addr & 0xFF; + addr = i2c_10bit_addr_lo_from_msg(msg); if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0) return -EREMOTEIO; if (msg->flags & I2C_M_RD) { /* For read, send restart without stop condition */ - brcmstb_set_i2c_start_stop(dev, COND_RESTART - | COND_NOSTOP); + brcmstb_set_i2c_start_stop(dev, COND_RESTART | COND_NOSTOP); + /* Then re-send the first byte with the read bit set */ - addr = 0xF0 | ((msg->addr & 0x300) >> 7) | 0x01; + addr = i2c_10bit_addr_hi_from_msg(msg); if (brcmstb_i2c_write_data_byte(dev, &addr, 0) < 0) return -EREMOTEIO; - } } else { addr = i2c_8bit_addr_from_msg(msg); -- cgit v1.2.3-59-g8ed1b From 3bf28fab4ec5b60970183e99428b6c497a75df21 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 16:07:19 +0200 Subject: i2c: eg20t: Use i2c_10bit_addr_*_from_msg() helpers Use i2c_10bit_addr_*_from_msg() helpers instead of local copy. No functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213141045.2716943-6-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-eg20t.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 4914bfbee2a9..efdaddf99f9e 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -48,8 +48,6 @@ #define BUS_IDLE_TIMEOUT 20 #define PCH_I2CCTL_I2CMEN 0x0080 -#define TEN_BIT_ADDR_DEFAULT 0xF000 -#define TEN_BIT_ADDR_MASK 0xF0 #define PCH_START 0x0020 #define PCH_RESTART 0x0004 #define PCH_ESR_START 0x0001 @@ -58,7 +56,6 @@ #define PCH_ACK 0x0008 #define PCH_GETACK 0x0001 #define CLR_REG 0x0 -#define I2C_RD 0x1 #define I2CMCF_BIT 0x0080 #define I2CMIF_BIT 0x0002 #define I2CMAL_BIT 0x0010 @@ -76,8 +73,6 @@ #define I2CMBB_BIT 0x0020 #define BUFFER_MODE_MASK (I2CBMFI_BIT | I2CBMAL_BIT | I2CBMNA_BIT | \ I2CBMTO_BIT | I2CBMIS_BIT) -#define I2C_ADDR_MSK 0xFF -#define I2C_MSB_2B_MSK 0x300 #define FAST_MODE_CLK 400 #define FAST_MODE_EN 0x0001 #define SUB_ADDR_LEN_MAX 4 @@ -371,16 +366,12 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, struct i2c_algo_pch_data *adap = i2c_adap->algo_data; u8 *buf; u32 length; - u32 addr; - u32 addr_2_msb; - u32 addr_8_lsb; s32 wrcount; s32 rtn; void __iomem *p = adap->pch_base_address; length = msgs->len; buf = msgs->buf; - addr = msgs->addr; /* enable master tx */ pch_setbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE); @@ -394,8 +385,7 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, } if (msgs->flags & I2C_M_TEN) { - addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7) & 0x06; - iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); + iowrite32(i2c_10bit_addr_hi_from_msg(msgs), p + PCH_I2CDR); if (first) pch_i2c_start(adap); @@ -403,8 +393,7 @@ static s32 pch_i2c_writebytes(struct i2c_adapter *i2c_adap, if (rtn) return rtn; - addr_8_lsb = (addr & I2C_ADDR_MSK); - iowrite32(addr_8_lsb, p + PCH_I2CDR); + iowrite32(i2c_10bit_addr_lo_from_msg(msgs), p + PCH_I2CDR); } else { /* set 7 bit slave address and R/W bit as 0 */ iowrite32(i2c_8bit_addr_from_msg(msgs), p + PCH_I2CDR); @@ -490,15 +479,11 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, u8 *buf; u32 count; u32 length; - u32 addr; - u32 addr_2_msb; - u32 addr_8_lsb; void __iomem *p = adap->pch_base_address; s32 rtn; length = msgs->len; buf = msgs->buf; - addr = msgs->addr; /* enable master reception */ pch_clrbit(adap->pch_base_address, PCH_I2CCTL, I2C_TX_MODE); @@ -509,8 +494,7 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, } if (msgs->flags & I2C_M_TEN) { - addr_2_msb = ((addr & I2C_MSB_2B_MSK) >> 7); - iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); + iowrite32(i2c_10bit_addr_hi_from_msg(msgs) & ~I2C_M_RD, p + PCH_I2CDR); if (first) pch_i2c_start(adap); @@ -518,8 +502,7 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, if (rtn) return rtn; - addr_8_lsb = (addr & I2C_ADDR_MSK); - iowrite32(addr_8_lsb, p + PCH_I2CDR); + iowrite32(i2c_10bit_addr_lo_from_msg(msgs), p + PCH_I2CDR); pch_i2c_restart(adap); @@ -527,8 +510,7 @@ static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, if (rtn) return rtn; - addr_2_msb |= I2C_RD; - iowrite32(addr_2_msb | TEN_BIT_ADDR_MASK, p + PCH_I2CDR); + iowrite32(i2c_10bit_addr_hi_from_msg(msgs), p + PCH_I2CDR); } else { /* 7 address bits + R/W bit */ iowrite32(i2c_8bit_addr_from_msg(msgs), p + PCH_I2CDR); -- cgit v1.2.3-59-g8ed1b From ed7f48d3efa0943c0563c1657ae3b24e9ebbf568 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 16:07:20 +0200 Subject: i2c: kempld: Use i2c_10bit_addr_*_from_msg() helpers Use i2c_10bit_addr_*_from_msg() helpers instead of local copy. No functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213141045.2716943-7-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-kempld.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-kempld.c b/drivers/i2c/busses/i2c-kempld.c index 212196af68ba..9b4c7cba62b6 100644 --- a/drivers/i2c/busses/i2c-kempld.c +++ b/drivers/i2c/busses/i2c-kempld.c @@ -115,9 +115,7 @@ static int kempld_i2c_process(struct kempld_i2c_data *i2c) if (i2c->state == STATE_ADDR) { /* 10 bit address? */ if (i2c->msg->flags & I2C_M_TEN) { - addr = 0xf0 | ((i2c->msg->addr >> 7) & 0x6); - /* Set read bit if necessary */ - addr |= (i2c->msg->flags & I2C_M_RD) ? 1 : 0; + addr = i2c_10bit_addr_hi_from_msg(msg); i2c->state = STATE_ADDR10; } else { addr = i2c_8bit_addr_from_msg(i2c->msg); @@ -132,10 +130,12 @@ static int kempld_i2c_process(struct kempld_i2c_data *i2c) /* Second part of 10 bit addressing */ if (i2c->state == STATE_ADDR10) { - kempld_write8(pld, KEMPLD_I2C_DATA, i2c->msg->addr & 0xff); + addr = i2c_10bit_addr_lo_from_msg(msg); + i2c->state = STATE_START; + + kempld_write8(pld, KEMPLD_I2C_DATA, addr); kempld_write8(pld, KEMPLD_I2C_CMD, I2C_CMD_WRITE); - i2c->state = STATE_START; return 0; } -- cgit v1.2.3-59-g8ed1b From 6cdc8fe0ba423b002029b00daee8ea296abb7494 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 16:07:21 +0200 Subject: i2c: mt7621: Use i2c_10bit_addr_*_from_msg() helpers Use i2c_10bit_addr_*_from_msg() helpers instead of local copy. No functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213141045.2716943-8-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-mt7621.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-mt7621.c b/drivers/i2c/busses/i2c-mt7621.c index 2103f21f9ddd..0a288c998419 100644 --- a/drivers/i2c/busses/i2c-mt7621.c +++ b/drivers/i2c/busses/i2c-mt7621.c @@ -164,22 +164,18 @@ static int mtk_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, /* write address */ if (pmsg->flags & I2C_M_TEN) { /* 10 bits address */ - addr = 0xf0 | ((pmsg->addr >> 7) & 0x06); - addr |= (pmsg->addr & 0xff) << 8; - if (pmsg->flags & I2C_M_RD) - addr |= 1; - iowrite32(addr, i2c->base + REG_SM0D0_REG); - ret = mtk_i2c_cmd(i2c, SM0CTL1_WRITE, 2); - if (ret) - goto err_timeout; + addr = i2c_10bit_addr_hi_from_msg(pmsg); + addr |= i2c_10bit_addr_lo_from_msg(pmsg) << 8; + len = 2; } else { /* 7 bits address */ addr = i2c_8bit_addr_from_msg(pmsg); - iowrite32(addr, i2c->base + REG_SM0D0_REG); - ret = mtk_i2c_cmd(i2c, SM0CTL1_WRITE, 1); - if (ret) - goto err_timeout; + len = 1; } + iowrite32(addr, i2c->base + REG_SM0D0_REG); + ret = mtk_i2c_cmd(i2c, SM0CTL1_WRITE, len); + if (ret) + goto err_timeout; /* check address ACK */ if (!(pmsg->flags & I2C_M_IGNORE_NAK)) { -- cgit v1.2.3-59-g8ed1b From dbb1c2edb5eab197f9d65fbc1ea0a500643cf1ca Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 16:07:22 +0200 Subject: i2c: rzv2m: Use i2c_10bit_addr_*_from_msg() helpers Use i2c_10bit_addr_*_from_msg() helpers instead of local copy. No functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213141045.2716943-9-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-rzv2m.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-rzv2m.c b/drivers/i2c/busses/i2c-rzv2m.c index 02b76e24a476..53762cc56d28 100644 --- a/drivers/i2c/busses/i2c-rzv2m.c +++ b/drivers/i2c/busses/i2c-rzv2m.c @@ -287,20 +287,15 @@ static int rzv2m_i2c_send_address(struct rzv2m_i2c_priv *priv, int ret; if (msg->flags & I2C_M_TEN) { - /* - * 10-bit address - * addr_1: 5'b11110 | addr[9:8] | (R/nW) - * addr_2: addr[7:0] - */ - addr = 0xf0 | ((msg->addr & GENMASK(9, 8)) >> 7); - addr |= !!(msg->flags & I2C_M_RD); - /* Send 1st address(extend code) */ + /* 10-bit address: Send 1st address(extend code) */ + addr = i2c_10bit_addr_hi_from_msg(msg); ret = rzv2m_i2c_write_with_ack(priv, addr); if (ret) return ret; - /* Send 2nd address */ - ret = rzv2m_i2c_write_with_ack(priv, msg->addr & 0xff); + /* 10-bit address: Send 2nd address */ + addr = i2c_10bit_addr_lo_from_msg(msg); + ret = rzv2m_i2c_write_with_ack(priv, addr); } else { /* 7-bit address */ addr = i2c_8bit_addr_from_msg(msg); -- cgit v1.2.3-59-g8ed1b From 2ee4415274fadbb24ce7d6a0bd88e1d752b2b553 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 16:07:23 +0200 Subject: i2c: ibm_iic: Use i2c_*bit_addr*_from_msg() helpers Use i2c_*bit_addr*_from_msg() helpers instead of local copy. No functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213141045.2716943-10-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-ibm_iic.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index c76c4116ddc7..6bf45d752ff9 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -512,19 +512,17 @@ static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm, static inline void iic_address(struct ibm_iic_private* dev, struct i2c_msg* msg) { volatile struct iic_regs __iomem *iic = dev->vaddr; - u16 addr = msg->addr; DBG2("%d: iic_address, 0x%03x (%d-bit)\n", dev->idx, - addr, msg->flags & I2C_M_TEN ? 10 : 7); + msg->addr, msg->flags & I2C_M_TEN ? 10 : 7); - if (msg->flags & I2C_M_TEN){ + if (msg->flags & I2C_M_TEN) { out_8(&iic->cntl, CNTL_AMD); - out_8(&iic->lmadr, addr); - out_8(&iic->hmadr, 0xf0 | ((addr >> 7) & 0x06)); - } - else { + out_8(&iic->lmadr, i2c_10bit_addr_lo_from_msg(msg)); + out_8(&iic->hmadr, i2c_10bit_addr_hi_from_msg(msg) & ~I2C_M_RD); + } else { out_8(&iic->cntl, 0); - out_8(&iic->lmadr, addr << 1); + out_8(&iic->lmadr, i2c_8bit_addr_from_msg(msg) & ~I2C_M_RD); } } -- cgit v1.2.3-59-g8ed1b From f2157d1f7aaad18b95b4f73cc8225e8e8de1f737 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 13 Feb 2025 16:07:24 +0200 Subject: i2c: mv64xxx: Use i2c_*bit_addr*_from_msg() helpers Use i2c_*bit_addr*_from_msg() helpers instead of local copy. No functional change intended. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250213141045.2716943-11-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-mv64xxx.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 874309580c33..8fc26a511320 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -27,7 +27,6 @@ #include #include -#define MV64XXX_I2C_ADDR_ADDR(val) ((val & 0x7f) << 1) #define MV64XXX_I2C_BAUD_DIV_N(val) (val & 0x7) #define MV64XXX_I2C_BAUD_DIV_M(val) ((val & 0xf) << 3) @@ -176,22 +175,17 @@ static void mv64xxx_i2c_prepare_for_io(struct mv64xxx_i2c_data *drv_data, struct i2c_msg *msg) { - u32 dir = 0; - drv_data->cntl_bits = MV64XXX_I2C_REG_CONTROL_ACK | MV64XXX_I2C_REG_CONTROL_TWSIEN; if (!drv_data->atomic) drv_data->cntl_bits |= MV64XXX_I2C_REG_CONTROL_INTEN; - if (msg->flags & I2C_M_RD) - dir = 1; - if (msg->flags & I2C_M_TEN) { - drv_data->addr1 = 0xf0 | (((u32)msg->addr & 0x300) >> 7) | dir; - drv_data->addr2 = (u32)msg->addr & 0xff; + drv_data->addr1 = i2c_10bit_addr_hi_from_msg(msg); + drv_data->addr2 = i2c_10bit_addr_lo_from_msg(msg); } else { - drv_data->addr1 = MV64XXX_I2C_ADDR_ADDR((u32)msg->addr) | dir; + drv_data->addr1 = i2c_8bit_addr_from_msg(msg); drv_data->addr2 = 0; } } -- cgit v1.2.3-59-g8ed1b From 9e2fd53073cb52f57d40713dbecd649dee4f3c0a Mon Sep 17 00:00:00 2001 From: Anindya Sundar Gayen Date: Fri, 28 Feb 2025 19:07:45 +0530 Subject: i2c: i2c-exynos5: fixed a spelling error Corrected a spelling mistake in the i2c-exynos5 driver to improve code readability. No functional changes were made. Signed-off-by: Anindya Sundar Gayen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250228133745.35053-1-anindya.sg@samsung.com --- drivers/i2c/busses/i2c-exynos5.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index 6cdd957ea7e4..02f24479aa07 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -814,7 +814,7 @@ static int exynos5_i2c_xfer_msg(struct exynos5_i2c *i2c, ret = i2c->state; /* - * If this is the last message to be transfered (stop == 1) + * If this is the last message to be transferred (stop == 1) * Then check if the bus can be brought back to idle. */ if (ret == 0 && stop) -- cgit v1.2.3-59-g8ed1b From 3d36dd1161ca3158f600d6dbeab8b645e56d1d92 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Thu, 6 Feb 2025 17:27:07 +0530 Subject: i2c: cadence: Simplify using devm_clk_get_enabled() Clock handling can be very simplified with using devm_clk_get_enabled() as was done by commit 8d2aaf4382b7 ("gpio: zynq: Simplify using devm_clk_get_enabled()"). And also fix issue in connection to incorrect sequence when err_clk_dis label is called. When reset_control_deassert() fails it jumps to err_clk_dis label which disables clock and also disable pm_runtime setup but nothing has been setup at this time of failure because initialization is done below reset_control_deassert() call. Signed-off-by: Michal Simek Signed-off-by: Manikanta Guntupalli Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250206115708.1085523-2-manikanta.guntupalli@amd.com --- drivers/i2c/busses/i2c-cadence.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index b64026fbca66..51dc7728d133 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -1541,7 +1541,7 @@ static int cdns_i2c_probe(struct platform_device *pdev) snprintf(id->adap.name, sizeof(id->adap.name), "Cadence I2C at %08lx", (unsigned long)r_mem->start); - id->clk = devm_clk_get(&pdev->dev, NULL); + id->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(id->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(id->clk), "input clock not found.\n"); @@ -1551,16 +1551,10 @@ static int cdns_i2c_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(id->reset), "Failed to request reset.\n"); - ret = clk_prepare_enable(id->clk); - if (ret) - dev_err(&pdev->dev, "Unable to enable clock.\n"); - ret = reset_control_deassert(id->reset); - if (ret) { - dev_err_probe(&pdev->dev, ret, - "Failed to de-assert reset.\n"); - goto err_clk_dis; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to de-assert reset.\n"); pm_runtime_set_autosuspend_delay(id->dev, CNDS_I2C_PM_TIMEOUT); pm_runtime_use_autosuspend(id->dev); @@ -1616,8 +1610,6 @@ static int cdns_i2c_probe(struct platform_device *pdev) err_clk_notifier_unregister: clk_notifier_unregister(id->clk, &id->clk_rate_change_nb); reset_control_assert(id->reset); -err_clk_dis: - clk_disable_unprepare(id->clk); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); return ret; @@ -1642,7 +1634,6 @@ static void cdns_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&id->adap); clk_notifier_unregister(id->clk, &id->clk_rate_change_nb); reset_control_assert(id->reset); - clk_disable_unprepare(id->clk); } static struct platform_driver cdns_i2c_drv = { -- cgit v1.2.3-59-g8ed1b From 61b804548e17447b2a777650a8ff0708707f0cb9 Mon Sep 17 00:00:00 2001 From: Manikanta Guntupalli Date: Thu, 6 Feb 2025 17:27:08 +0530 Subject: i2c: cadence: Move reset_control_assert after pm_runtime_set_suspended in probe error path Ensure reset_control_assert() is called after pm_runtime_set_suspended() in the cdns_i2c_probe exit path to maintain proper power management sequence in error cases. Signed-off-by: Manikanta Guntupalli Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250206115708.1085523-3-manikanta.guntupalli@amd.com --- drivers/i2c/busses/i2c-cadence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 51dc7728d133..8df63aaf2a80 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -1609,9 +1609,9 @@ static int cdns_i2c_probe(struct platform_device *pdev) err_clk_notifier_unregister: clk_notifier_unregister(id->clk, &id->clk_rate_change_nb); - reset_control_assert(id->reset); pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); + reset_control_assert(id->reset); return ret; } -- cgit v1.2.3-59-g8ed1b From 9d515bf71ea2de9fefd74bb792f997857dd2f6b3 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 21 Feb 2025 21:28:21 +0100 Subject: i2c: i801: Cosmetic improvements - Use pci_err et al instead of dev_err to simplify the code - use format %pr to print resource Signed-off-by: Heiner Kallweit Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/6676001a-a584-46e2-a98e-17163d82c218@gmail.com --- drivers/i2c/busses/i2c-i801.c | 49 ++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 171d29d2770e..a3d79a5ab570 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -412,16 +412,14 @@ static int i801_check_post(struct i801_priv *priv, int status) /* Check if it worked */ status = inb_p(SMBHSTSTS(priv)); - if ((status & SMBHSTSTS_HOST_BUSY) || - !(status & SMBHSTSTS_FAILED)) - dev_dbg(&priv->pci_dev->dev, - "Failed terminating the transaction\n"); + if ((status & SMBHSTSTS_HOST_BUSY) || !(status & SMBHSTSTS_FAILED)) + pci_dbg(priv->pci_dev, "Failed terminating the transaction\n"); return -ETIMEDOUT; } if (status & SMBHSTSTS_FAILED) { result = -EIO; - dev_err(&priv->pci_dev->dev, "Transaction failed\n"); + pci_err(priv->pci_dev, "Transaction failed\n"); } if (status & SMBHSTSTS_DEV_ERR) { /* @@ -449,7 +447,7 @@ static int i801_check_post(struct i801_priv *priv, int status) } if (status & SMBHSTSTS_BUS_ERR) { result = -EAGAIN; - dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n"); + pci_dbg(priv->pci_dev, "Lost arbitration\n"); } return result; @@ -578,8 +576,7 @@ static void i801_isr_byte_done(struct i801_priv *priv) if (priv->count < priv->len) priv->data[priv->count++] = inb(SMBBLKDAT(priv)); else - dev_dbg(&priv->pci_dev->dev, - "Discarding extra byte on block read\n"); + pci_dbg(priv->pci_dev, "Discarding extra byte on block read\n"); /* Set LAST_BYTE for last byte of read transaction */ if (priv->count == priv->len - 1) @@ -1438,7 +1435,7 @@ static void i801_add_tco(struct i801_priv *priv) priv->tco_pdev = i801_add_tco_spt(pci_dev, tco_res); if (IS_ERR(priv->tco_pdev)) - dev_warn(&pci_dev->dev, "failed to create iTCO device\n"); + pci_warn(pci_dev, "failed to create iTCO device\n"); } #ifdef CONFIG_ACPI @@ -1467,8 +1464,8 @@ i801_acpi_io_handler(u32 function, acpi_physical_address address, u32 bits, if (!priv->acpi_reserved && i801_acpi_is_smbus_ioport(priv, address)) { priv->acpi_reserved = true; - dev_warn(&pdev->dev, "BIOS is accessing SMBus registers\n"); - dev_warn(&pdev->dev, "Driver SMBus register access inhibited\n"); + pci_warn(pdev, "BIOS is accessing SMBus registers\n"); + pci_warn(pdev, "Driver SMBus register access inhibited\n"); /* * BIOS is accessing the host controller so prevent it from @@ -1549,8 +1546,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) /* Disable features on user request */ for (i = 0; i < ARRAY_SIZE(i801_feature_names); i++) { if (priv->features & disable_features & (1 << i)) - dev_notice(&dev->dev, "%s disabled by user\n", - i801_feature_names[i]); + pci_notice(dev, "%s disabled by user\n", i801_feature_names[i]); } priv->features &= ~disable_features; @@ -1564,16 +1560,14 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) */ err = pci_enable_device(dev); if (err) { - dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n", - err); + pci_err(dev, "Failed to enable SMBus PCI device (%d)\n", err); return err; } /* Determine the address of the SMBus area */ priv->smba = pci_resource_start(dev, SMBBAR); if (!priv->smba) { - dev_err(&dev->dev, - "SMBus base address uninitialized, upgrade BIOS\n"); + pci_err(dev, "SMBus base address uninitialized, upgrade BIOS\n"); return -ENODEV; } @@ -1582,26 +1576,24 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) err = pcim_iomap_regions(dev, 1 << SMBBAR, DRV_NAME); if (err) { - dev_err(&dev->dev, - "Failed to request SMBus region 0x%lx-0x%Lx\n", - priv->smba, - (unsigned long long)pci_resource_end(dev, SMBBAR)); + pci_err(dev, "Failed to request SMBus region %pr\n", + pci_resource_n(dev, SMBBAR)); i801_acpi_remove(priv); return err; } - pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &priv->original_hstcfg); + pci_read_config_byte(dev, SMBHSTCFG, &priv->original_hstcfg); i801_setup_hstcfg(priv); if (!(priv->original_hstcfg & SMBHSTCFG_HST_EN)) - dev_info(&dev->dev, "Enabling SMBus device\n"); + pci_info(dev, "Enabling SMBus device\n"); if (priv->original_hstcfg & SMBHSTCFG_SMB_SMI_EN) { - dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n"); + pci_dbg(dev, "SMBus using interrupt SMI#\n"); /* Disable SMBus interrupt feature if SMBus using SMI# */ priv->features &= ~FEATURE_IRQ; } if (priv->original_hstcfg & SMBHSTCFG_SPD_WD) - dev_info(&dev->dev, "SPD Write Disable is set\n"); + pci_info(dev, "SPD Write Disable is set\n"); /* Clear special mode bits */ if (priv->features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER)) @@ -1620,7 +1612,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) /* Complain if an interrupt is already pending */ pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists); if (pcists & PCI_STATUS_INTERRUPT) - dev_warn(&dev->dev, "An interrupt is pending!\n"); + pci_warn(dev, "An interrupt is pending!\n"); } if (priv->features & FEATURE_IRQ) { @@ -1629,12 +1621,11 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) err = devm_request_irq(&dev->dev, dev->irq, i801_isr, IRQF_SHARED, DRV_NAME, priv); if (err) { - dev_err(&dev->dev, "Failed to allocate irq %d: %d\n", - dev->irq, err); + pci_err(dev, "Failed to allocate irq %d: %d\n", dev->irq, err); priv->features &= ~FEATURE_IRQ; } } - dev_info(&dev->dev, "SMBus using %s\n", + pci_info(dev, "SMBus using %s\n", priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling"); /* Host notification uses an interrupt */ -- cgit v1.2.3-59-g8ed1b From e5befb5b01bc6b570c47a421284ca5193d36280d Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 21 Feb 2025 21:29:23 +0100 Subject: i2c: i801: Move i801_wait_intr and i801_wait_byte_done in the code Move both functions to avoid forward declarations in a subsequent patch. Signed-off-by: Heiner Kallweit Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/a60ee54b-c5e8-4bdb-9f1f-8889f4dcd114@gmail.com --- drivers/i2c/busses/i2c-i801.c | 68 +++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index a3d79a5ab570..9097bb9cdb9e 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -337,6 +337,40 @@ MODULE_PARM_DESC(disable_features, "Disable selected driver features:\n" "\t\t 0x10 don't use interrupts\n" "\t\t 0x20 disable SMBus Host Notify "); +/* Wait for BUSY being cleared and either INTR or an error flag being set */ +static int i801_wait_intr(struct i801_priv *priv) +{ + unsigned long timeout = jiffies + priv->adapter.timeout; + int status, busy; + + do { + usleep_range(250, 500); + status = inb_p(SMBHSTSTS(priv)); + busy = status & SMBHSTSTS_HOST_BUSY; + status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR; + if (!busy && status) + return status & STATUS_ERROR_FLAGS; + } while (time_is_after_eq_jiffies(timeout)); + + return -ETIMEDOUT; +} + +/* Wait for either BYTE_DONE or an error flag being set */ +static int i801_wait_byte_done(struct i801_priv *priv) +{ + unsigned long timeout = jiffies + priv->adapter.timeout; + int status; + + do { + usleep_range(250, 500); + status = inb_p(SMBHSTSTS(priv)); + if (status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) + return status & STATUS_ERROR_FLAGS; + } while (time_is_after_eq_jiffies(timeout)); + + return -ETIMEDOUT; +} + static int i801_get_block_len(struct i801_priv *priv) { u8 len = inb_p(SMBHSTDAT0(priv)); @@ -453,40 +487,6 @@ static int i801_check_post(struct i801_priv *priv, int status) return result; } -/* Wait for BUSY being cleared and either INTR or an error flag being set */ -static int i801_wait_intr(struct i801_priv *priv) -{ - unsigned long timeout = jiffies + priv->adapter.timeout; - int status, busy; - - do { - usleep_range(250, 500); - status = inb_p(SMBHSTSTS(priv)); - busy = status & SMBHSTSTS_HOST_BUSY; - status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR; - if (!busy && status) - return status & STATUS_ERROR_FLAGS; - } while (time_is_after_eq_jiffies(timeout)); - - return -ETIMEDOUT; -} - -/* Wait for either BYTE_DONE or an error flag being set */ -static int i801_wait_byte_done(struct i801_priv *priv) -{ - unsigned long timeout = jiffies + priv->adapter.timeout; - int status; - - do { - usleep_range(250, 500); - status = inb_p(SMBHSTSTS(priv)); - if (status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) - return status & STATUS_ERROR_FLAGS; - } while (time_is_after_eq_jiffies(timeout)); - - return -ETIMEDOUT; -} - static int i801_transaction(struct i801_priv *priv, int xact) { unsigned long result; -- cgit v1.2.3-59-g8ed1b From 3a3c6b7b0387d12b067052e1027cd967fae8378a Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 21 Feb 2025 21:30:46 +0100 Subject: i2c: i801: Improve too small kill wait time in i801_check_post In my tests terminating a transaction took about 25ms, what is in line with the chip-internal timeout as described in 5.21.3.2 "Bus Time Out" in [0]. Therefore the 2ms delay is too low. Instead of a fixed delay let's use i801_wait_intr() here, this also facilitates the status handling. This potential issue seems to have been existing forever, but as no related problem is known, treat it as an improvement. [0] Intel document #326776-003, 7 Series PCH datasheet Signed-off-by: Heiner Kallweit Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/ad4ef645-5d03-4833-a0b6-f31f8fd06483@gmail.com --- drivers/i2c/busses/i2c-i801.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 9097bb9cdb9e..6a4147054b49 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -441,12 +441,11 @@ static int i801_check_post(struct i801_priv *priv, int status) if (unlikely(status < 0)) { /* try to stop the current command */ outb_p(SMBHSTCNT_KILL, SMBHSTCNT(priv)); - usleep_range(1000, 2000); + status = i801_wait_intr(priv); outb_p(0, SMBHSTCNT(priv)); /* Check if it worked */ - status = inb_p(SMBHSTSTS(priv)); - if ((status & SMBHSTSTS_HOST_BUSY) || !(status & SMBHSTSTS_FAILED)) + if (status < 0 || !(status & SMBHSTSTS_FAILED)) pci_dbg(priv->pci_dev, "Failed terminating the transaction\n"); return -ETIMEDOUT; } -- cgit v1.2.3-59-g8ed1b From 4a3f77ea77013d8d0178503a8abff33d8c4ba5c5 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 12 Mar 2025 20:07:23 +0100 Subject: i2c: i801: Switch to iomapped register access Switch to iomapped register access as a prerequisite for adding support for MMIO register access. This changes replaces the delayed inb_p()/outb_p() calls with calls to ioread8()/iowrite8() which don't have this extra delay. According to Documentation/driver-api/device-io.rst the _p versions are needed for ISA device access only, therefore switching to the non-delayed versions should not cause problems. However a certain risk remains, which on the other hand is significantly reduced by the fact that recent systems will use MMIO instead of PIO. ICH7 datasheet from 2012 mentions already that SMBus register space is also memory-mapped. So all systems from at least the last 10 yrs should be safe. Signed-off-by: Heiner Kallweit Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/67535b17-c3fb-4507-b083-9c1884b4dd7d@gmail.com --- drivers/i2c/busses/i2c-i801.c | 149 +++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 76 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 6a4147054b49..bf5702ccb93a 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -276,7 +276,7 @@ struct i801_mux_config { struct i801_priv { struct i2c_adapter adapter; - unsigned long smba; + void __iomem *smba; unsigned char original_hstcfg; unsigned char original_hstcnt; unsigned char original_slvcmd; @@ -345,7 +345,7 @@ static int i801_wait_intr(struct i801_priv *priv) do { usleep_range(250, 500); - status = inb_p(SMBHSTSTS(priv)); + status = ioread8(SMBHSTSTS(priv)); busy = status & SMBHSTSTS_HOST_BUSY; status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR; if (!busy && status) @@ -363,7 +363,7 @@ static int i801_wait_byte_done(struct i801_priv *priv) do { usleep_range(250, 500); - status = inb_p(SMBHSTSTS(priv)); + status = ioread8(SMBHSTSTS(priv)); if (status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) return status & STATUS_ERROR_FLAGS; } while (time_is_after_eq_jiffies(timeout)); @@ -373,7 +373,7 @@ static int i801_wait_byte_done(struct i801_priv *priv) static int i801_get_block_len(struct i801_priv *priv) { - u8 len = inb_p(SMBHSTDAT0(priv)); + u8 len = ioread8(SMBHSTDAT0(priv)); if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { pci_err(priv->pci_dev, "Illegal SMBus block read size %u\n", len); @@ -390,9 +390,9 @@ static int i801_check_and_clear_pec_error(struct i801_priv *priv) if (!(priv->features & FEATURE_SMBUS_PEC)) return 0; - status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE; + status = ioread8(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE; if (status) { - outb_p(status, SMBAUXSTS(priv)); + iowrite8(status, SMBAUXSTS(priv)); return -EBADMSG; } @@ -405,7 +405,7 @@ static int i801_check_pre(struct i801_priv *priv) { int status, result; - status = inb_p(SMBHSTSTS(priv)); + status = ioread8(SMBHSTSTS(priv)); if (status & SMBHSTSTS_HOST_BUSY) { pci_err(priv->pci_dev, "SMBus is busy, can't use it!\n"); return -EBUSY; @@ -414,7 +414,7 @@ static int i801_check_pre(struct i801_priv *priv) status &= STATUS_FLAGS; if (status) { pci_dbg(priv->pci_dev, "Clearing status flags (%02x)\n", status); - outb_p(status, SMBHSTSTS(priv)); + iowrite8(status, SMBHSTSTS(priv)); } /* @@ -440,9 +440,9 @@ static int i801_check_post(struct i801_priv *priv, int status) */ if (unlikely(status < 0)) { /* try to stop the current command */ - outb_p(SMBHSTCNT_KILL, SMBHSTCNT(priv)); + iowrite8(SMBHSTCNT_KILL, SMBHSTCNT(priv)); status = i801_wait_intr(priv); - outb_p(0, SMBHSTCNT(priv)); + iowrite8(0, SMBHSTCNT(priv)); /* Check if it worked */ if (status < 0 || !(status & SMBHSTSTS_FAILED)) @@ -493,13 +493,13 @@ static int i801_transaction(struct i801_priv *priv, int xact) if (priv->features & FEATURE_IRQ) { reinit_completion(&priv->done); - outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START, + iowrite8(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START, SMBHSTCNT(priv)); result = wait_for_completion_timeout(&priv->done, adap->timeout); return result ? priv->status : -ETIMEDOUT; } - outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv)); + iowrite8(xact | SMBHSTCNT_START, SMBHSTCNT(priv)); return i801_wait_intr(priv); } @@ -508,7 +508,7 @@ static int i801_block_transaction_by_block(struct i801_priv *priv, union i2c_smbus_data *data, char read_write, int command) { - int i, len, status, xact; + int len, status, xact; switch (command) { case I2C_SMBUS_BLOCK_PROC_CALL: @@ -522,14 +522,13 @@ static int i801_block_transaction_by_block(struct i801_priv *priv, } /* Set block buffer mode */ - outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv)); + iowrite8(ioread8(SMBAUXCTL(priv)) | SMBAUXCTL_E32B, SMBAUXCTL(priv)); if (read_write == I2C_SMBUS_WRITE) { len = data->block[0]; - outb_p(len, SMBHSTDAT0(priv)); - inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */ - for (i = 0; i < len; i++) - outb_p(data->block[i+1], SMBBLKDAT(priv)); + iowrite8(len, SMBHSTDAT0(priv)); + ioread8(SMBHSTCNT(priv)); /* reset the data buffer index */ + iowrite8_rep(SMBBLKDAT(priv), data->block + 1, len); } status = i801_transaction(priv, xact); @@ -545,12 +544,11 @@ static int i801_block_transaction_by_block(struct i801_priv *priv, } data->block[0] = len; - inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */ - for (i = 0; i < len; i++) - data->block[i + 1] = inb_p(SMBBLKDAT(priv)); + ioread8(SMBHSTCNT(priv)); /* reset the data buffer index */ + ioread8_rep(SMBBLKDAT(priv), data->block + 1, len); } out: - outb_p(inb_p(SMBAUXCTL(priv)) & ~SMBAUXCTL_E32B, SMBAUXCTL(priv)); + iowrite8(ioread8(SMBAUXCTL(priv)) & ~SMBAUXCTL_E32B, SMBAUXCTL(priv)); return status; } @@ -573,17 +571,17 @@ static void i801_isr_byte_done(struct i801_priv *priv) /* Read next byte */ if (priv->count < priv->len) - priv->data[priv->count++] = inb(SMBBLKDAT(priv)); + priv->data[priv->count++] = ioread8(SMBBLKDAT(priv)); else pci_dbg(priv->pci_dev, "Discarding extra byte on block read\n"); /* Set LAST_BYTE for last byte of read transaction */ if (priv->count == priv->len - 1) - outb_p(priv->cmd | SMBHSTCNT_LAST_BYTE, + iowrite8(priv->cmd | SMBHSTCNT_LAST_BYTE, SMBHSTCNT(priv)); } else if (priv->count < priv->len - 1) { /* Write next byte, except for IRQ after last byte */ - outb_p(priv->data[++priv->count], SMBBLKDAT(priv)); + iowrite8(priv->data[++priv->count], SMBBLKDAT(priv)); } } @@ -591,7 +589,7 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv) { unsigned short addr; - addr = inb_p(SMBNTFDADD(priv)) >> 1; + addr = ioread8(SMBNTFDADD(priv)) >> 1; /* * With the tested platforms, reading SMBNTFDDAT (22 + (p)->smba) @@ -601,7 +599,7 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv) i2c_handle_smbus_host_notify(&priv->adapter, addr); /* clear Host Notify bit and return */ - outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv)); + iowrite8(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv)); return IRQ_HANDLED; } @@ -632,12 +630,12 @@ static irqreturn_t i801_isr(int irq, void *dev_id) return IRQ_NONE; if (priv->features & FEATURE_HOST_NOTIFY) { - status = inb_p(SMBSLVSTS(priv)); + status = ioread8(SMBSLVSTS(priv)); if (status & SMBSLVSTS_HST_NTFY_STS) return i801_host_notify_isr(priv); } - status = inb_p(SMBHSTSTS(priv)); + status = ioread8(SMBHSTSTS(priv)); if ((status & (SMBHSTSTS_BYTE_DONE | STATUS_ERROR_FLAGS)) == SMBHSTSTS_BYTE_DONE) i801_isr_byte_done(priv); @@ -647,7 +645,7 @@ static irqreturn_t i801_isr(int irq, void *dev_id) * so clear it always when the status is set. */ status &= STATUS_FLAGS | SMBHSTSTS_SMBALERT_STS; - outb_p(status, SMBHSTSTS(priv)); + iowrite8(status, SMBHSTSTS(priv)); status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR; if (status) { @@ -679,8 +677,8 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, len = data->block[0]; if (read_write == I2C_SMBUS_WRITE) { - outb_p(len, SMBHSTDAT0(priv)); - outb_p(data->block[1], SMBBLKDAT(priv)); + iowrite8(len, SMBHSTDAT0(priv)); + iowrite8(data->block[1], SMBBLKDAT(priv)); } if (command == I2C_SMBUS_I2C_BLOCK_DATA && @@ -699,14 +697,14 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, priv->data = &data->block[1]; reinit_completion(&priv->done); - outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv)); + iowrite8(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv)); result = wait_for_completion_timeout(&priv->done, adap->timeout); return result ? priv->status : -ETIMEDOUT; } if (len == 1 && read_write == I2C_SMBUS_READ) smbcmd |= SMBHSTCNT_LAST_BYTE; - outb_p(smbcmd | SMBHSTCNT_START, SMBHSTCNT(priv)); + iowrite8(smbcmd | SMBHSTCNT_START, SMBHSTCNT(priv)); for (i = 1; i <= len; i++) { status = i801_wait_byte_done(priv); @@ -722,27 +720,27 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, len = i801_get_block_len(priv); if (len < 0) { /* Recover */ - while (inb_p(SMBHSTSTS(priv)) & + while (ioread8(SMBHSTSTS(priv)) & SMBHSTSTS_HOST_BUSY) - outb_p(SMBHSTSTS_BYTE_DONE, + iowrite8(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); - outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); + iowrite8(SMBHSTSTS_INTR, SMBHSTSTS(priv)); return -EPROTO; } data->block[0] = len; } if (read_write == I2C_SMBUS_READ) { - data->block[i] = inb_p(SMBBLKDAT(priv)); + data->block[i] = ioread8(SMBBLKDAT(priv)); if (i == len - 1) - outb_p(smbcmd | SMBHSTCNT_LAST_BYTE, SMBHSTCNT(priv)); + iowrite8(smbcmd | SMBHSTCNT_LAST_BYTE, SMBHSTCNT(priv)); } if (read_write == I2C_SMBUS_WRITE && i+1 <= len) - outb_p(data->block[i+1], SMBBLKDAT(priv)); + iowrite8(data->block[i+1], SMBBLKDAT(priv)); /* signals SMBBLKDAT ready */ - outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); + iowrite8(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); } return i801_wait_intr(priv); @@ -750,7 +748,7 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write) { - outb_p((addr << 1) | (read_write & 0x01), SMBHSTADD(priv)); + iowrite8((addr << 1) | (read_write & 0x01), SMBHSTADD(priv)); } /* Single value transaction function */ @@ -767,30 +765,30 @@ static int i801_simple_transaction(struct i801_priv *priv, union i2c_smbus_data case I2C_SMBUS_BYTE: i801_set_hstadd(priv, addr, read_write); if (read_write == I2C_SMBUS_WRITE) - outb_p(hstcmd, SMBHSTCMD(priv)); + iowrite8(hstcmd, SMBHSTCMD(priv)); xact = I801_BYTE; break; case I2C_SMBUS_BYTE_DATA: i801_set_hstadd(priv, addr, read_write); if (read_write == I2C_SMBUS_WRITE) - outb_p(data->byte, SMBHSTDAT0(priv)); - outb_p(hstcmd, SMBHSTCMD(priv)); + iowrite8(data->byte, SMBHSTDAT0(priv)); + iowrite8(hstcmd, SMBHSTCMD(priv)); xact = I801_BYTE_DATA; break; case I2C_SMBUS_WORD_DATA: i801_set_hstadd(priv, addr, read_write); if (read_write == I2C_SMBUS_WRITE) { - outb_p(data->word & 0xff, SMBHSTDAT0(priv)); - outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv)); + iowrite8(data->word & 0xff, SMBHSTDAT0(priv)); + iowrite8((data->word & 0xff00) >> 8, SMBHSTDAT1(priv)); } - outb_p(hstcmd, SMBHSTCMD(priv)); + iowrite8(hstcmd, SMBHSTCMD(priv)); xact = I801_WORD_DATA; break; case I2C_SMBUS_PROC_CALL: i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE); - outb_p(data->word & 0xff, SMBHSTDAT0(priv)); - outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv)); - outb_p(hstcmd, SMBHSTCMD(priv)); + iowrite8(data->word & 0xff, SMBHSTDAT0(priv)); + iowrite8((data->word & 0xff00) >> 8, SMBHSTDAT1(priv)); + iowrite8(hstcmd, SMBHSTCMD(priv)); read_write = I2C_SMBUS_READ; xact = I801_PROC_CALL; break; @@ -806,12 +804,12 @@ static int i801_simple_transaction(struct i801_priv *priv, union i2c_smbus_data switch (command) { case I2C_SMBUS_BYTE: case I2C_SMBUS_BYTE_DATA: - data->byte = inb_p(SMBHSTDAT0(priv)); + data->byte = ioread8(SMBHSTDAT0(priv)); break; case I2C_SMBUS_WORD_DATA: case I2C_SMBUS_PROC_CALL: - data->word = inb_p(SMBHSTDAT0(priv)) + - (inb_p(SMBHSTDAT1(priv)) << 8); + data->word = ioread8(SMBHSTDAT0(priv)) + + (ioread8(SMBHSTDAT1(priv)) << 8); break; } @@ -832,7 +830,7 @@ static int i801_smbus_block_transaction(struct i801_priv *priv, union i2c_smbus_ i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE); else i801_set_hstadd(priv, addr, read_write); - outb_p(hstcmd, SMBHSTCMD(priv)); + iowrite8(hstcmd, SMBHSTCMD(priv)); if (priv->features & FEATURE_BLOCK_BUFFER) return i801_block_transaction_by_block(priv, data, read_write, command); @@ -858,9 +856,9 @@ static int i801_i2c_block_transaction(struct i801_priv *priv, union i2c_smbus_da /* NB: page 240 of ICH5 datasheet shows that DATA1 is the cmd field when reading */ if (read_write == I2C_SMBUS_READ) - outb_p(hstcmd, SMBHSTDAT1(priv)); + iowrite8(hstcmd, SMBHSTDAT1(priv)); else - outb_p(hstcmd, SMBHSTCMD(priv)); + iowrite8(hstcmd, SMBHSTCMD(priv)); if (read_write == I2C_SMBUS_WRITE) { /* set I2C_EN bit in configuration register */ @@ -903,9 +901,9 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, && size != I2C_SMBUS_I2C_BLOCK_DATA; if (hwpec) /* enable/disable hardware PEC */ - outb_p(inb_p(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv)); + iowrite8(ioread8(SMBAUXCTL(priv)) | SMBAUXCTL_CRC, SMBAUXCTL(priv)); else - outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC), + iowrite8(ioread8(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC), SMBAUXCTL(priv)); if (size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_BLOCK_PROC_CALL) @@ -921,13 +919,13 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, * time, so we forcibly disable it after every transaction. */ if (hwpec) - outb_p(inb_p(SMBAUXCTL(priv)) & ~SMBAUXCTL_CRC, SMBAUXCTL(priv)); + iowrite8(ioread8(SMBAUXCTL(priv)) & ~SMBAUXCTL_CRC, SMBAUXCTL(priv)); out: /* * Unlock the SMBus device for use by BIOS/ACPI, * and clear status flags if not done already. */ - outb_p(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); + iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); pm_runtime_mark_last_busy(&priv->pci_dev->dev); pm_runtime_put_autosuspend(&priv->pci_dev->dev); @@ -964,11 +962,11 @@ static void i801_enable_host_notify(struct i2c_adapter *adapter) * from the SMB_ALERT signal because the driver does not support * SMBus Alert. */ - outb_p(SMBSLVCMD_HST_NTFY_INTREN | SMBSLVCMD_SMBALERT_DISABLE | + iowrite8(SMBSLVCMD_HST_NTFY_INTREN | SMBSLVCMD_SMBALERT_DISABLE | priv->original_slvcmd, SMBSLVCMD(priv)); /* clear Host Notify bit to allow a new notification */ - outb_p(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv)); + iowrite8(SMBSLVSTS_HST_NTFY_STS, SMBSLVSTS(priv)); } static void i801_disable_host_notify(struct i801_priv *priv) @@ -976,7 +974,7 @@ static void i801_disable_host_notify(struct i801_priv *priv) if (!(priv->features & FEATURE_HOST_NOTIFY)) return; - outb_p(priv->original_slvcmd, SMBSLVCMD(priv)); + iowrite8(priv->original_slvcmd, SMBSLVCMD(priv)); } static const struct i2c_algorithm smbus_algorithm = { @@ -1441,7 +1439,7 @@ static void i801_add_tco(struct i801_priv *priv) static bool i801_acpi_is_smbus_ioport(const struct i801_priv *priv, acpi_physical_address address) { - return address >= priv->smba && + return address >= pci_resource_start(priv->pci_dev, SMBBAR) && address <= pci_resource_end(priv->pci_dev, SMBBAR); } @@ -1518,7 +1516,7 @@ static void i801_setup_hstcfg(struct i801_priv *priv) static void i801_restore_regs(struct i801_priv *priv) { - outb_p(priv->original_hstcnt, SMBHSTCNT(priv)); + iowrite8(priv->original_hstcnt, SMBHSTCNT(priv)); pci_write_config_byte(priv->pci_dev, SMBHSTCFG, priv->original_hstcfg); } @@ -1564,8 +1562,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) } /* Determine the address of the SMBus area */ - priv->smba = pci_resource_start(dev, SMBBAR); - if (!priv->smba) { + if (!pci_resource_start(dev, SMBBAR)) { pci_err(dev, "SMBus base address uninitialized, upgrade BIOS\n"); return -ENODEV; } @@ -1573,12 +1570,12 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) if (i801_acpi_probe(priv)) return -ENODEV; - err = pcim_iomap_regions(dev, 1 << SMBBAR, DRV_NAME); - if (err) { + priv->smba = pcim_iomap_region(dev, SMBBAR, DRV_NAME); + if (IS_ERR(priv->smba)) { pci_err(dev, "Failed to request SMBus region %pr\n", pci_resource_n(dev, SMBBAR)); i801_acpi_remove(priv); - return err; + return PTR_ERR(priv->smba); } pci_read_config_byte(dev, SMBHSTCFG, &priv->original_hstcfg); @@ -1596,7 +1593,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) /* Clear special mode bits */ if (priv->features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER)) - outb_p(inb_p(SMBAUXCTL(priv)) & + iowrite8(ioread8(SMBAUXCTL(priv)) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); /* Default timeout in interrupt mode: 200 ms */ @@ -1632,9 +1629,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) priv->features &= ~FEATURE_HOST_NOTIFY; /* Remember original Interrupt and Host Notify settings */ - priv->original_hstcnt = inb_p(SMBHSTCNT(priv)) & ~SMBHSTCNT_KILL; + priv->original_hstcnt = ioread8(SMBHSTCNT(priv)) & ~SMBHSTCNT_KILL; if (priv->features & FEATURE_HOST_NOTIFY) - priv->original_slvcmd = inb_p(SMBSLVCMD(priv)); + priv->original_slvcmd = ioread8(SMBSLVCMD(priv)); i801_add_tco(priv); @@ -1643,9 +1640,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) * to instantiante i2c_clients, do not change. */ snprintf(priv->adapter.name, sizeof(priv->adapter.name), - "SMBus %s adapter at %04lx", + "SMBus %s adapter at %s", (priv->features & FEATURE_IDF) ? "I801 IDF" : "I801", - priv->smba); + pci_name(dev)); err = i2c_add_adapter(&priv->adapter); if (err) { -- cgit v1.2.3-59-g8ed1b From d50f2f5d51ea609194439e6e3f470d1a4fad761b Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 12 Mar 2025 20:08:18 +0100 Subject: i2c: i801: Use MMIO if available Newer versions of supported chips support MMIO in addition to legacy PMIO register access. Probe the MMIO PCI BAR and use faster MMIO register access if available. Signed-off-by: Heiner Kallweit Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/b4748b7a-aac5-445c-b813-20c4d2c23ec3@gmail.com --- drivers/i2c/busses/i2c-i801.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index bf5702ccb93a..48e1af544b75 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -144,6 +144,7 @@ #define SMBNTFDADD(p) (20 + (p)->smba) /* ICH3 and later */ /* PCI Address Constants */ +#define SMBBAR_MMIO 0 #define SMBBAR 4 #define SMBHSTCFG 0x040 #define TCOBASE 0x050 @@ -1522,7 +1523,7 @@ static void i801_restore_regs(struct i801_priv *priv) static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) { - int err, i; + int err, i, bar = SMBBAR; struct i801_priv *priv; priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL); @@ -1570,10 +1571,13 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) if (i801_acpi_probe(priv)) return -ENODEV; - priv->smba = pcim_iomap_region(dev, SMBBAR, DRV_NAME); + if (pci_resource_flags(dev, SMBBAR_MMIO) & IORESOURCE_MEM) + bar = SMBBAR_MMIO; + + priv->smba = pcim_iomap_region(dev, bar, DRV_NAME); if (IS_ERR(priv->smba)) { pci_err(dev, "Failed to request SMBus region %pr\n", - pci_resource_n(dev, SMBBAR)); + pci_resource_n(dev, bar)); i801_acpi_remove(priv); return PTR_ERR(priv->smba); } -- cgit v1.2.3-59-g8ed1b From 7202745e29f860114331b431906a6854117b4167 Mon Sep 17 00:00:00 2001 From: Aryan Srivastava Date: Tue, 18 Mar 2025 15:16:29 +1300 Subject: i2c: octeon: fix return commenting Kernel-docs require a ':' to signify the return behaviour of a function with within the comment. Many functions in this file were missing ':' after the "Returns" line, resulting in kernel-doc warnings. Add the ':' to satisfy kernel-doc requirements. Signed-off-by: Aryan Srivastava Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250318021632.2710792-2-aryan.srivastava@alliedtelesis.co.nz Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-octeon-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c index 3fbc828508ab..0094fe5f7460 100644 --- a/drivers/i2c/busses/i2c-octeon-core.c +++ b/drivers/i2c/busses/i2c-octeon-core.c @@ -45,7 +45,7 @@ static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c) * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c * - * Returns 0 on success, otherwise a negative errno. + * Returns: 0 on success, otherwise a negative errno. */ static int octeon_i2c_wait(struct octeon_i2c *i2c) { @@ -139,7 +139,7 @@ static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c) * octeon_i2c_hlc_wait - wait for an HLC operation to complete * @i2c: The struct octeon_i2c * - * Returns 0 on success, otherwise -ETIMEDOUT. + * Returns: 0 on success, otherwise -ETIMEDOUT. */ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { @@ -273,7 +273,7 @@ static int octeon_i2c_recovery(struct octeon_i2c *i2c) * octeon_i2c_start - send START to the bus * @i2c: The struct octeon_i2c * - * Returns 0 on success, otherwise a negative errno. + * Returns: 0 on success, otherwise a negative errno. */ static int octeon_i2c_start(struct octeon_i2c *i2c) { @@ -314,7 +314,7 @@ static void octeon_i2c_stop(struct octeon_i2c *i2c) * * The address is sent over the bus, then the data is read. * - * Returns 0 on success, otherwise a negative errno. + * Returns: 0 on success, otherwise a negative errno. */ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, u16 *rlength, bool recv_len) @@ -382,7 +382,7 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, * * The address is sent over the bus, then the data. * - * Returns 0 on success, otherwise a negative errno. + * Returns: 0 on success, otherwise a negative errno. */ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, const u8 *data, int length) @@ -625,7 +625,7 @@ err: * @msgs: Pointer to the messages to be processed * @num: Length of the MSGS array * - * Returns the number of messages processed, or a negative errno on failure. + * Returns: the number of messages processed, or a negative errno on failure. */ int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) { -- cgit v1.2.3-59-g8ed1b From b1c010bd25f8ba5fd09c617daed2fb03343f1f67 Mon Sep 17 00:00:00 2001 From: Aryan Srivastava Date: Tue, 18 Mar 2025 15:16:30 +1300 Subject: i2c: octeon: remove 10-bit addressing support The driver gives the illusion of 10-bit address support, but the upper 3 bits of the given address are always thrown away. Remove unnecessary considerations for 10 bit addressing and always complete 7 bit ops when using the hlc methods. Signed-off-by: Aryan Srivastava Reviewed-by: Wolfram Sang Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250318021632.2710792-3-aryan.srivastava@alliedtelesis.co.nz Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-octeon-core.c | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c index 0094fe5f7460..baf6b27f3752 100644 --- a/drivers/i2c/busses/i2c-octeon-core.c +++ b/drivers/i2c/busses/i2c-octeon-core.c @@ -421,17 +421,12 @@ static int octeon_i2c_hlc_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) octeon_i2c_hlc_enable(i2c); octeon_i2c_hlc_int_clear(i2c); - cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR; + cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR | SW_TWSI_OP_7; /* SIZE */ cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT; /* A */ cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; - if (msgs[0].flags & I2C_M_TEN) - cmd |= SW_TWSI_OP_10; - else - cmd |= SW_TWSI_OP_7; - octeon_i2c_writeq_flush(cmd, i2c->twsi_base + OCTEON_REG_SW_TWSI(i2c)); ret = octeon_i2c_hlc_wait(i2c); if (ret) @@ -463,17 +458,12 @@ static int octeon_i2c_hlc_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) octeon_i2c_hlc_enable(i2c); octeon_i2c_hlc_int_clear(i2c); - cmd = SW_TWSI_V | SW_TWSI_SOVR; + cmd = SW_TWSI_V | SW_TWSI_SOVR | SW_TWSI_OP_7; /* SIZE */ cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT; /* A */ cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_ADDR_SHIFT; - if (msgs[0].flags & I2C_M_TEN) - cmd |= SW_TWSI_OP_10; - else - cmd |= SW_TWSI_OP_7; - for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--) cmd |= (u64)msgs[0].buf[j] << (8 * i); @@ -513,11 +503,6 @@ static bool octeon_i2c_hlc_ext(struct octeon_i2c *i2c, struct i2c_msg msg, u64 * bool set_ext = false; u64 cmd = 0; - if (msg.flags & I2C_M_TEN) - cmd |= SW_TWSI_OP_10_IA; - else - cmd |= SW_TWSI_OP_7_IA; - if (msg.len == 2) { cmd |= SW_TWSI_EIA; *ext = (u64)msg.buf[0] << SW_TWSI_IA_SHIFT; @@ -550,7 +535,7 @@ static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs octeon_i2c_hlc_enable(i2c); - cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR; + cmd = SW_TWSI_V | SW_TWSI_R | SW_TWSI_SOVR | SW_TWSI_OP_7_IA; /* SIZE */ cmd |= (u64)(msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT; /* A */ @@ -587,7 +572,7 @@ static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msg octeon_i2c_hlc_enable(i2c); - cmd = SW_TWSI_V | SW_TWSI_SOVR; + cmd = SW_TWSI_V | SW_TWSI_SOVR | SW_TWSI_OP_7_IA; /* SIZE */ cmd |= (u64)(msgs[1].len - 1) << SW_TWSI_SIZE_SHIFT; /* A */ -- cgit v1.2.3-59-g8ed1b From b6ef830c60b6f4adfb72d0780b4363df3a1feb9c Mon Sep 17 00:00:00 2001 From: Jayesh Choudhary Date: Tue, 18 Mar 2025 16:06:22 +0530 Subject: i2c: omap: Add support for setting mux Some SoCs require muxes in the routing for SDA and SCL lines. Therefore, add support for setting the mux by reading the mux-states property from the dt-node. Signed-off-by: Jayesh Choudhary Link: https://lore.kernel.org/r/20250318103622.29979-3-j-choudhary@ti.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-omap.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index fc438f445771..0648e58b083e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -940,6 +940,7 @@ config I2C_OMAP tristate "OMAP I2C adapter" depends on ARCH_OMAP || ARCH_K3 || COMPILE_TEST default MACH_OMAP_OSK + select MULTIPLEXER help If you say yes to this option, support will be included for the I2C interface on the Texas Instruments OMAP1/2 family of processors. diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index f18c3e74b076..16afb9ca19bb 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -211,6 +212,7 @@ struct omap_i2c_dev { u16 syscstate; u16 westate; u16 errata; + struct mux_state *mux_state; }; static const u8 reg_map_ip_v1[] = { @@ -1452,6 +1454,23 @@ omap_i2c_probe(struct platform_device *pdev) (1000 * omap->speed / 8); } + if (of_property_read_bool(node, "mux-states")) { + struct mux_state *mux_state; + + mux_state = devm_mux_state_get(&pdev->dev, NULL); + if (IS_ERR(mux_state)) { + r = PTR_ERR(mux_state); + dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r); + goto err_disable_pm; + } + omap->mux_state = mux_state; + r = mux_state_select(omap->mux_state); + if (r) { + dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r); + goto err_disable_pm; + } + } + /* reset ASAP, clearing any IRQs */ omap_i2c_init(omap); @@ -1511,6 +1530,9 @@ static void omap_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&omap->adapter); + if (omap->mux_state) + mux_state_deselect(omap->mux_state); + ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) dev_err(omap->dev, "Failed to resume hardware, skip disable\n"); -- cgit v1.2.3-59-g8ed1b From 5ea558473fa31f4eeb5c76d58277a7ab68fd501d Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Wed, 19 Mar 2025 17:29:00 +0800 Subject: i2c: spacemit: add support for SpacemiT K1 SoC This patch introduces basic I2C support for the SpacemiT K1 SoC, utilizing interrupts for transfers. The driver has been tested using i2c-tools on a Bananapi-F3 board, and basic I2C read/write operations have been confirmed to work. Signed-off-by: Troy Mitchell Reviewed-by: Alex Elder Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250319-k1-i2c-master-v8-2-013e2df2b78d@gmail.com --- drivers/i2c/busses/Kconfig | 17 ++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-k1.c | 602 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 620 insertions(+) create mode 100644 drivers/i2c/busses/i2c-k1.c (limited to 'drivers') diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 0648e58b083e..83c88c79afe2 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -783,6 +783,23 @@ config I2C_JZ4780 If you don't know what to do here, say N. +config I2C_K1 + tristate "SpacemiT K1 I2C adapter" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on OF + help + This option enables support for the I2C interface on the SpacemiT K1 + platform. + + If you enable this configuration, the kernel will include support for + the I2C adapter specific to the SpacemiT K1 platform. This driver can + be used to manage I2C bus transactions, which are necessary for + interfacing with I2C peripherals such as sensors, EEPROMs, and other + devices. + + This driver can also be built as a module. If so, the + module will be called `i2c-k1`. + config I2C_KEBA tristate "KEBA I2C controller support" depends on HAS_IOMEM diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 1c2a4510abe4..c1252e2b779e 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_I2C_IMX) += i2c-imx.o obj-$(CONFIG_I2C_IMX_LPI2C) += i2c-imx-lpi2c.o obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o obj-$(CONFIG_I2C_JZ4780) += i2c-jz4780.o +obj-$(CONFIG_I2C_K1) += i2c-k1.o obj-$(CONFIG_I2C_KEBA) += i2c-keba.o obj-$(CONFIG_I2C_KEMPLD) += i2c-kempld.o obj-$(CONFIG_I2C_LPC2K) += i2c-lpc2k.o diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c new file mode 100644 index 000000000000..0d8763b852db --- /dev/null +++ b/drivers/i2c/busses/i2c-k1.c @@ -0,0 +1,602 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2024-2025 Troy Mitchell + */ + + #include + #include + #include + #include + #include + #include + +/* spacemit i2c registers */ +#define SPACEMIT_ICR 0x0 /* Control register */ +#define SPACEMIT_ISR 0x4 /* Status register */ +#define SPACEMIT_IDBR 0xc /* Data buffer register */ +#define SPACEMIT_IBMR 0x1c /* Bus monitor register */ + +/* SPACEMIT_ICR register fields */ +#define SPACEMIT_CR_START BIT(0) /* start bit */ +#define SPACEMIT_CR_STOP BIT(1) /* stop bit */ +#define SPACEMIT_CR_ACKNAK BIT(2) /* send ACK(0) or NAK(1) */ +#define SPACEMIT_CR_TB BIT(3) /* transfer byte bit */ +/* Bits 4-7 are reserved */ +#define SPACEMIT_CR_MODE_FAST BIT(8) /* bus mode (master operation) */ +/* Bit 9 is reserved */ +#define SPACEMIT_CR_UR BIT(10) /* unit reset */ +/* Bits 11-12 are reserved */ +#define SPACEMIT_CR_SCLE BIT(13) /* master clock enable */ +#define SPACEMIT_CR_IUE BIT(14) /* unit enable */ +/* Bits 15-17 are reserved */ +#define SPACEMIT_CR_ALDIE BIT(18) /* enable arbitration interrupt */ +#define SPACEMIT_CR_DTEIE BIT(19) /* enable TX interrupts */ +#define SPACEMIT_CR_DRFIE BIT(20) /* enable RX interrupts */ +#define SPACEMIT_CR_GCD BIT(21) /* general call disable */ +#define SPACEMIT_CR_BEIE BIT(22) /* enable bus error ints */ +/* Bits 23-24 are reserved */ +#define SPACEMIT_CR_MSDIE BIT(25) /* master STOP detected int enable */ +#define SPACEMIT_CR_MSDE BIT(26) /* master STOP detected enable */ +#define SPACEMIT_CR_TXDONEIE BIT(27) /* transaction done int enable */ +#define SPACEMIT_CR_TXEIE BIT(28) /* transmit FIFO empty int enable */ +#define SPACEMIT_CR_RXHFIE BIT(29) /* receive FIFO half-full int enable */ +#define SPACEMIT_CR_RXFIE BIT(30) /* receive FIFO full int enable */ +#define SPACEMIT_CR_RXOVIE BIT(31) /* receive FIFO overrun int enable */ + +#define SPACEMIT_I2C_INT_CTRL_MASK (SPACEMIT_CR_ALDIE | SPACEMIT_CR_DTEIE | \ + SPACEMIT_CR_DRFIE | SPACEMIT_CR_BEIE | \ + SPACEMIT_CR_TXDONEIE | SPACEMIT_CR_TXEIE | \ + SPACEMIT_CR_RXHFIE | SPACEMIT_CR_RXFIE | \ + SPACEMIT_CR_RXOVIE | SPACEMIT_CR_MSDIE) + +/* SPACEMIT_ISR register fields */ +/* Bits 0-13 are reserved */ +#define SPACEMIT_SR_ACKNAK BIT(14) /* ACK/NACK status */ +#define SPACEMIT_SR_UB BIT(15) /* unit busy */ +#define SPACEMIT_SR_IBB BIT(16) /* i2c bus busy */ +#define SPACEMIT_SR_EBB BIT(17) /* early bus busy */ +#define SPACEMIT_SR_ALD BIT(18) /* arbitration loss detected */ +#define SPACEMIT_SR_ITE BIT(19) /* TX buffer empty */ +#define SPACEMIT_SR_IRF BIT(20) /* RX buffer full */ +#define SPACEMIT_SR_GCAD BIT(21) /* general call address detected */ +#define SPACEMIT_SR_BED BIT(22) /* bus error no ACK/NAK */ +#define SPACEMIT_SR_SAD BIT(23) /* slave address detected */ +#define SPACEMIT_SR_SSD BIT(24) /* slave stop detected */ +/* Bit 25 is reserved */ +#define SPACEMIT_SR_MSD BIT(26) /* master stop detected */ +#define SPACEMIT_SR_TXDONE BIT(27) /* transaction done */ +#define SPACEMIT_SR_TXE BIT(28) /* TX FIFO empty */ +#define SPACEMIT_SR_RXHF BIT(29) /* RX FIFO half-full */ +#define SPACEMIT_SR_RXF BIT(30) /* RX FIFO full */ +#define SPACEMIT_SR_RXOV BIT(31) /* RX FIFO overrun */ + +#define SPACEMIT_I2C_INT_STATUS_MASK (SPACEMIT_SR_RXOV | SPACEMIT_SR_RXF | SPACEMIT_SR_RXHF | \ + SPACEMIT_SR_TXE | SPACEMIT_SR_TXDONE | SPACEMIT_SR_MSD | \ + SPACEMIT_SR_SSD | SPACEMIT_SR_SAD | SPACEMIT_SR_BED | \ + SPACEMIT_SR_GCAD | SPACEMIT_SR_IRF | SPACEMIT_SR_ITE | \ + SPACEMIT_SR_ALD) + +/* SPACEMIT_IBMR register fields */ +#define SPACEMIT_BMR_SDA BIT(0) /* SDA line level */ +#define SPACEMIT_BMR_SCL BIT(1) /* SCL line level */ + +/* i2c bus recover timeout: us */ +#define SPACEMIT_I2C_BUS_BUSY_TIMEOUT 100000 + +#define SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ 100000 /* Hz */ +#define SPACEMIT_I2C_MAX_FAST_MODE_FREQ 400000 /* Hz */ + +#define SPACEMIT_SR_ERR (SPACEMIT_SR_BED | SPACEMIT_SR_RXOV | SPACEMIT_SR_ALD) + +enum spacemit_i2c_state { + SPACEMIT_STATE_IDLE, + SPACEMIT_STATE_START, + SPACEMIT_STATE_READ, + SPACEMIT_STATE_WRITE, +}; + +/* i2c-spacemit driver's main struct */ +struct spacemit_i2c_dev { + struct device *dev; + struct i2c_adapter adapt; + + /* hardware resources */ + void __iomem *base; + int irq; + u32 clock_freq; + + struct i2c_msg *msgs; + u32 msg_num; + + /* index of the current message being processed */ + u32 msg_idx; + u8 *msg_buf; + /* the number of unprocessed bytes remaining in the current message */ + u32 unprocessed; + + enum spacemit_i2c_state state; + bool read; + struct completion complete; + u32 status; +}; + +static void spacemit_i2c_enable(struct spacemit_i2c_dev *i2c) +{ + u32 val; + + val = readl(i2c->base + SPACEMIT_ICR); + val |= SPACEMIT_CR_IUE; + writel(val, i2c->base + SPACEMIT_ICR); +} + +static void spacemit_i2c_disable(struct spacemit_i2c_dev *i2c) +{ + u32 val; + + val = readl(i2c->base + SPACEMIT_ICR); + val &= ~SPACEMIT_CR_IUE; + writel(val, i2c->base + SPACEMIT_ICR); +} + +static void spacemit_i2c_reset(struct spacemit_i2c_dev *i2c) +{ + writel(SPACEMIT_CR_UR, i2c->base + SPACEMIT_ICR); + udelay(5); + writel(0, i2c->base + SPACEMIT_ICR); +} + +static int spacemit_i2c_handle_err(struct spacemit_i2c_dev *i2c) +{ + dev_dbg(i2c->dev, "i2c error status: 0x%08x\n", i2c->status); + + if (i2c->status & (SPACEMIT_SR_BED | SPACEMIT_SR_ALD)) { + spacemit_i2c_reset(i2c); + return -EAGAIN; + } + + return i2c->status & SPACEMIT_SR_ACKNAK ? -ENXIO : -EIO; +} + +static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c) +{ + u32 status; + + /* if bus is locked, reset unit. 0: locked */ + status = readl(i2c->base + SPACEMIT_IBMR); + if ((status & SPACEMIT_BMR_SDA) && (status & SPACEMIT_BMR_SCL)) + return; + + spacemit_i2c_reset(i2c); + usleep_range(10, 20); + + /* check scl status again */ + status = readl(i2c->base + SPACEMIT_IBMR); + if (!(status & SPACEMIT_BMR_SCL)) + dev_warn_ratelimited(i2c->dev, "unit reset failed\n"); +} + +static int spacemit_i2c_wait_bus_idle(struct spacemit_i2c_dev *i2c) +{ + int ret; + u32 val; + + val = readl(i2c->base + SPACEMIT_ISR); + if (!(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB))) + return 0; + + ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR, + val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)), + 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT); + if (ret) + spacemit_i2c_reset(i2c); + + return ret; +} + +static void spacemit_i2c_check_bus_release(struct spacemit_i2c_dev *i2c) +{ + /* in case bus is not released after transfer completes */ + if (readl(i2c->base + SPACEMIT_ISR) & SPACEMIT_SR_EBB) { + spacemit_i2c_conditionally_reset_bus(i2c); + usleep_range(90, 150); + } +} + +static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) +{ + u32 val; + + /* + * Unmask interrupt bits for all xfer mode: + * bus error, arbitration loss detected. + * For transaction complete signal, we use master stop + * interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE. + */ + val = SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE; + + /* + * Unmask interrupt bits for interrupt xfer mode: + * When IDBR receives a byte, an interrupt is triggered. + * + * For the tx empty interrupt, it will be enabled in the + * i2c_start function. + * Otherwise, it will cause an erroneous empty interrupt before i2c_start. + */ + val |= SPACEMIT_CR_DRFIE; + + if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ) + val |= SPACEMIT_CR_MODE_FAST; + + /* disable response to general call */ + val |= SPACEMIT_CR_GCD; + + /* enable SCL clock output */ + val |= SPACEMIT_CR_SCLE; + + /* enable master stop detected */ + val |= SPACEMIT_CR_MSDE | SPACEMIT_CR_MSDIE; + + writel(val, i2c->base + SPACEMIT_ICR); +} + +static inline void +spacemit_i2c_clear_int_status(struct spacemit_i2c_dev *i2c, u32 mask) +{ + writel(mask & SPACEMIT_I2C_INT_STATUS_MASK, i2c->base + SPACEMIT_ISR); +} + +static void spacemit_i2c_start(struct spacemit_i2c_dev *i2c) +{ + u32 target_addr_rw, val; + struct i2c_msg *cur_msg = i2c->msgs + i2c->msg_idx; + + i2c->read = !!(cur_msg->flags & I2C_M_RD); + + i2c->state = SPACEMIT_STATE_START; + + target_addr_rw = (cur_msg->addr & 0x7f) << 1; + if (cur_msg->flags & I2C_M_RD) + target_addr_rw |= 1; + + writel(target_addr_rw, i2c->base + SPACEMIT_IDBR); + + /* send start pulse */ + val = readl(i2c->base + SPACEMIT_ICR); + val &= ~SPACEMIT_CR_STOP; + val |= SPACEMIT_CR_START | SPACEMIT_CR_TB | SPACEMIT_CR_DTEIE; + writel(val, i2c->base + SPACEMIT_ICR); +} + +static void spacemit_i2c_stop(struct spacemit_i2c_dev *i2c) +{ + u32 val; + + val = readl(i2c->base + SPACEMIT_ICR); + val |= SPACEMIT_CR_STOP | SPACEMIT_CR_ALDIE | SPACEMIT_CR_TB; + + if (i2c->read) + val |= SPACEMIT_CR_ACKNAK; + + writel(val, i2c->base + SPACEMIT_ICR); +} + +static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c) +{ + unsigned long time_left; + struct i2c_msg *msg; + + for (i2c->msg_idx = 0; i2c->msg_idx < i2c->msg_num; i2c->msg_idx++) { + msg = &i2c->msgs[i2c->msg_idx]; + i2c->msg_buf = msg->buf; + i2c->unprocessed = msg->len; + i2c->status = 0; + + reinit_completion(&i2c->complete); + + spacemit_i2c_start(i2c); + + time_left = wait_for_completion_timeout(&i2c->complete, + i2c->adapt.timeout); + if (!time_left) { + dev_err(i2c->dev, "msg completion timeout\n"); + spacemit_i2c_conditionally_reset_bus(i2c); + spacemit_i2c_reset(i2c); + return -ETIMEDOUT; + } + + if (i2c->status & SPACEMIT_SR_ERR) + return spacemit_i2c_handle_err(i2c); + } + + return 0; +} + +static bool spacemit_i2c_is_last_msg(struct spacemit_i2c_dev *i2c) +{ + if (i2c->msg_idx != i2c->msg_num - 1) + return false; + + if (i2c->read) + return i2c->unprocessed == 1; + + return !i2c->unprocessed; +} + +static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c) +{ + /* if transfer completes, SPACEMIT_ISR will handle it */ + if (i2c->status & SPACEMIT_SR_MSD) + return; + + if (i2c->unprocessed) { + writel(*i2c->msg_buf++, i2c->base + SPACEMIT_IDBR); + i2c->unprocessed--; + return; + } + + /* SPACEMIT_STATE_IDLE avoids trigger next byte */ + i2c->state = SPACEMIT_STATE_IDLE; + complete(&i2c->complete); +} + +static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c) +{ + if (i2c->unprocessed) { + *i2c->msg_buf++ = readl(i2c->base + SPACEMIT_IDBR); + i2c->unprocessed--; + } + + /* if transfer completes, SPACEMIT_ISR will handle it */ + if (i2c->status & (SPACEMIT_SR_MSD | SPACEMIT_SR_ACKNAK)) + return; + + /* it has to append stop bit in icr that read last byte */ + if (i2c->unprocessed) + return; + + /* SPACEMIT_STATE_IDLE avoids trigger next byte */ + i2c->state = SPACEMIT_STATE_IDLE; + complete(&i2c->complete); +} + +static void spacemit_i2c_handle_start(struct spacemit_i2c_dev *i2c) +{ + i2c->state = i2c->read ? SPACEMIT_STATE_READ : SPACEMIT_STATE_WRITE; + if (i2c->state == SPACEMIT_STATE_WRITE) + spacemit_i2c_handle_write(i2c); +} + +static void spacemit_i2c_err_check(struct spacemit_i2c_dev *i2c) +{ + u32 val; + + /* + * Send transaction complete signal: + * error happens, detect master stop + */ + if (!(i2c->status & (SPACEMIT_SR_ERR | SPACEMIT_SR_MSD))) + return; + + /* + * Here the transaction is already done, we don't need any + * other interrupt signals from now, in case any interrupt + * happens before spacemit_i2c_xfer to disable irq and i2c unit, + * we mask all the interrupt signals and clear the interrupt + * status. + */ + val = readl(i2c->base + SPACEMIT_ICR); + val &= ~SPACEMIT_I2C_INT_CTRL_MASK; + writel(val, i2c->base + SPACEMIT_ICR); + + spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); + + i2c->state = SPACEMIT_STATE_IDLE; + complete(&i2c->complete); +} + +static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) +{ + struct spacemit_i2c_dev *i2c = devid; + u32 status, val; + + status = readl(i2c->base + SPACEMIT_ISR); + if (!status) + return IRQ_HANDLED; + + i2c->status = status; + + spacemit_i2c_clear_int_status(i2c, status); + + if (i2c->status & SPACEMIT_SR_ERR) + goto err_out; + + val = readl(i2c->base + SPACEMIT_ICR); + val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | SPACEMIT_CR_STOP | SPACEMIT_CR_START); + writel(val, i2c->base + SPACEMIT_ICR); + + switch (i2c->state) { + case SPACEMIT_STATE_START: + spacemit_i2c_handle_start(i2c); + break; + case SPACEMIT_STATE_READ: + spacemit_i2c_handle_read(i2c); + break; + case SPACEMIT_STATE_WRITE: + spacemit_i2c_handle_write(i2c); + break; + default: + break; + } + + if (i2c->state != SPACEMIT_STATE_IDLE) { + if (spacemit_i2c_is_last_msg(i2c)) { + /* trigger next byte with stop */ + spacemit_i2c_stop(i2c); + } else { + /* trigger next byte */ + val |= SPACEMIT_CR_ALDIE | SPACEMIT_CR_TB; + writel(val, i2c->base + SPACEMIT_ICR); + } + } + +err_out: + spacemit_i2c_err_check(i2c); + return IRQ_HANDLED; +} + +static void spacemit_i2c_calc_timeout(struct spacemit_i2c_dev *i2c) +{ + unsigned long timeout; + int idx = 0, cnt = 0; + + for (; idx < i2c->msg_num; idx++) + cnt += (i2c->msgs + idx)->len + 1; + + /* + * Multiply by 9 because each byte in I2C transmission requires + * 9 clock cycles: 8 bits of data plus 1 ACK/NACK bit. + */ + timeout = cnt * 9 * USEC_PER_SEC / i2c->clock_freq; + + i2c->adapt.timeout = usecs_to_jiffies(timeout + USEC_PER_SEC / 10) / i2c->msg_num; +} + +static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) +{ + struct spacemit_i2c_dev *i2c = i2c_get_adapdata(adapt); + int ret; + + i2c->msgs = msgs; + i2c->msg_num = num; + + spacemit_i2c_calc_timeout(i2c); + + spacemit_i2c_init(i2c); + + spacemit_i2c_enable(i2c); + + ret = spacemit_i2c_wait_bus_idle(i2c); + if (!ret) + spacemit_i2c_xfer_msg(i2c); + else if (ret < 0) + dev_dbg(i2c->dev, "i2c transfer error: %d\n", ret); + else + spacemit_i2c_check_bus_release(i2c); + + spacemit_i2c_disable(i2c); + + if (ret == -ETIMEDOUT || ret == -EAGAIN) + dev_err(i2c->dev, "i2c transfer failed, ret %d err 0x%lx\n", + ret, i2c->status & SPACEMIT_SR_ERR); + + return ret < 0 ? ret : num; +} + +static u32 spacemit_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); +} + +static const struct i2c_algorithm spacemit_i2c_algo = { + .xfer = spacemit_i2c_xfer, + .functionality = spacemit_i2c_func, +}; + +static int spacemit_i2c_probe(struct platform_device *pdev) +{ + struct clk *clk; + struct device *dev = &pdev->dev; + struct device_node *of_node = pdev->dev.of_node; + struct spacemit_i2c_dev *i2c; + int ret; + + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq); + if (ret && ret != -EINVAL) + dev_warn(dev, "failed to read clock-frequency property: %d\n", ret); + + /* For now, this driver doesn't support high-speed. */ + if (!i2c->clock_freq || i2c->clock_freq > SPACEMIT_I2C_MAX_FAST_MODE_FREQ) { + dev_warn(dev, "unsupported clock frequency %u; using %u\n", + i2c->clock_freq, SPACEMIT_I2C_MAX_FAST_MODE_FREQ); + i2c->clock_freq = SPACEMIT_I2C_MAX_FAST_MODE_FREQ; + } else if (i2c->clock_freq < SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ) { + dev_warn(dev, "unsupported clock frequency %u; using %u\n", + i2c->clock_freq, SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ); + i2c->clock_freq = SPACEMIT_I2C_MAX_STANDARD_MODE_FREQ; + } + + i2c->dev = &pdev->dev; + + i2c->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(i2c->base)) + return dev_err_probe(dev, PTR_ERR(i2c->base), "failed to do ioremap"); + + i2c->irq = platform_get_irq(pdev, 0); + if (i2c->irq < 0) + return dev_err_probe(dev, i2c->irq, "failed to get irq resource"); + + ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler, + IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(i2c->dev), i2c); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq"); + + clk = devm_clk_get_enabled(dev, "func"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock"); + + clk = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to enable bus clock"); + + spacemit_i2c_reset(i2c); + + i2c_set_adapdata(&i2c->adapt, i2c); + i2c->adapt.owner = THIS_MODULE; + i2c->adapt.algo = &spacemit_i2c_algo; + i2c->adapt.dev.parent = i2c->dev; + i2c->adapt.nr = pdev->id; + + i2c->adapt.dev.of_node = of_node; + + strscpy(i2c->adapt.name, "spacemit-i2c-adapter", sizeof(i2c->adapt.name)); + + init_completion(&i2c->complete); + + platform_set_drvdata(pdev, i2c); + + ret = i2c_add_numbered_adapter(&i2c->adapt); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to add i2c adapter"); + + return 0; +} + +static void spacemit_i2c_remove(struct platform_device *pdev) +{ + struct spacemit_i2c_dev *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adapt); +} + +static const struct of_device_id spacemit_i2c_of_match[] = { + { .compatible = "spacemit,k1-i2c", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, spacemit_i2c_of_match); + +static struct platform_driver spacemit_i2c_driver = { + .probe = spacemit_i2c_probe, + .remove = spacemit_i2c_remove, + .driver = { + .name = "i2c-k1", + .of_match_table = spacemit_i2c_of_match, + }, +}; +module_platform_driver(spacemit_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("I2C bus driver for SpacemiT K1 SoC"); -- cgit v1.2.3-59-g8ed1b From 088b1ca970ba6cac141f684b7592ce56bd25e7ea Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Thu, 20 Mar 2025 12:35:21 +0100 Subject: i2c: k1: Initialize variable before use Commit 95a8ca229032 ("i2c: spacemit: add support for SpacemiT K1 SoC") introduced a check in the probe function to warn the user if the DTS has incorrect frequency settings. In such cases, the driver falls back to default values. However, the return value of of_property_read_u32() was not stored, making the check ineffective. Fix this by storing the return value in 'ret' and evaluating it properly. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202503200928.eBWfwnHG-lkp@intel.com/ Cc: Troy Mitchell Link: https://lore.kernel.org/r/20250320113521.3966762-1-andi.shyti@kernel.org Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-k1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index 0d8763b852db..5965b4cf6220 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -514,7 +514,7 @@ static int spacemit_i2c_probe(struct platform_device *pdev) if (!i2c) return -ENOMEM; - of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq); + ret = of_property_read_u32(of_node, "clock-frequency", &i2c->clock_freq); if (ret && ret != -EINVAL) dev_warn(dev, "failed to read clock-frequency property: %d\n", ret); -- cgit v1.2.3-59-g8ed1b From 8b4da3ef92060c281aa3c541ed7aab51500cf7c8 Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sat, 22 Feb 2025 13:38:33 +0000 Subject: i2c: pasemi: Add registers bits and switch to BIT() Add the missing register bits to the defines and also switch those to use the BIT macro which is much more readable than using hardcoded masks Co-developed-by: Hector Martin Signed-off-by: Hector Martin Signed-off-by: Sven Peter Reviewed-by: Neal Gompa Reviewed-by: Alyssa Rosenzweig Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250222-pasemi-fixes-v1-1-d7ea33d50c5e@svenpeter.dev --- drivers/i2c/busses/i2c-pasemi-core.c | 40 ++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-pasemi-core.c b/drivers/i2c/busses/i2c-pasemi-core.c index dac694a9d781..bd128ab2e2eb 100644 --- a/drivers/i2c/busses/i2c-pasemi-core.c +++ b/drivers/i2c/busses/i2c-pasemi-core.c @@ -5,6 +5,7 @@ * SMBus host driver for PA Semi PWRficient */ +#include #include #include #include @@ -26,21 +27,30 @@ #define REG_REV 0x28 /* Register defs */ -#define MTXFIFO_READ 0x00000400 -#define MTXFIFO_STOP 0x00000200 -#define MTXFIFO_START 0x00000100 -#define MTXFIFO_DATA_M 0x000000ff - -#define MRXFIFO_EMPTY 0x00000100 -#define MRXFIFO_DATA_M 0x000000ff - -#define SMSTA_XEN 0x08000000 -#define SMSTA_MTN 0x00200000 - -#define CTL_MRR 0x00000400 -#define CTL_MTR 0x00000200 -#define CTL_EN 0x00000800 -#define CTL_CLK_M 0x000000ff +#define MTXFIFO_READ BIT(10) +#define MTXFIFO_STOP BIT(9) +#define MTXFIFO_START BIT(8) +#define MTXFIFO_DATA_M GENMASK(7, 0) + +#define MRXFIFO_EMPTY BIT(8) +#define MRXFIFO_DATA_M GENMASK(7, 0) + +#define SMSTA_XIP BIT(28) +#define SMSTA_XEN BIT(27) +#define SMSTA_JMD BIT(25) +#define SMSTA_JAM BIT(24) +#define SMSTA_MTO BIT(23) +#define SMSTA_MTA BIT(22) +#define SMSTA_MTN BIT(21) +#define SMSTA_MRNE BIT(19) +#define SMSTA_MTE BIT(16) +#define SMSTA_TOM BIT(6) + +#define CTL_EN BIT(11) +#define CTL_MRR BIT(10) +#define CTL_MTR BIT(9) +#define CTL_UJM BIT(8) +#define CTL_CLK_M GENMASK(7, 0) static inline void reg_write(struct pasemi_smbus *smbus, int reg, int val) { -- cgit v1.2.3-59-g8ed1b From f8d311b4b8f34f3dd1e5b488547d7eb31bcc7083 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 12 Feb 2025 18:51:28 +0200 Subject: i2c: mlxbf: Use readl_poll_timeout_atomic() for polling Convert the usage of an open-coded custom tight poll while loop with the provided readl_poll_timeout_atomic() macro. Signed-off-by: Andy Shevchenko Reviewed-by: Asmaa Mnebhi Link: https://lore.kernel.org/r/20250212165128.2413430-1-andriy.shevchenko@linux.intel.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-mlxbf.c | 106 ++++++++++------------------------------- 1 file changed, 26 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-mlxbf.c b/drivers/i2c/busses/i2c-mlxbf.c index 21f67f3b65b6..280dde53d7f3 100644 --- a/drivers/i2c/busses/i2c-mlxbf.c +++ b/drivers/i2c/busses/i2c-mlxbf.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -495,65 +496,6 @@ static u8 mlxbf_i2c_bus_count; static struct mutex mlxbf_i2c_bus_lock; -/* - * Function to poll a set of bits at a specific address; it checks whether - * the bits are equal to zero when eq_zero is set to 'true', and not equal - * to zero when eq_zero is set to 'false'. - * Note that the timeout is given in microseconds. - */ -static u32 mlxbf_i2c_poll(void __iomem *io, u32 addr, u32 mask, - bool eq_zero, u32 timeout) -{ - u32 bits; - - timeout = (timeout / MLXBF_I2C_POLL_FREQ_IN_USEC) + 1; - - do { - bits = readl(io + addr) & mask; - if (eq_zero ? bits == 0 : bits != 0) - return eq_zero ? 1 : bits; - udelay(MLXBF_I2C_POLL_FREQ_IN_USEC); - } while (timeout-- != 0); - - return 0; -} - -/* - * SW must make sure that the SMBus Master GW is idle before starting - * a transaction. Accordingly, this function polls the Master FSM stop - * bit; it returns false when the bit is asserted, true if not. - */ -static bool mlxbf_i2c_smbus_master_wait_for_idle(struct mlxbf_i2c_priv *priv) -{ - u32 mask = MLXBF_I2C_SMBUS_MASTER_FSM_STOP_MASK; - u32 addr = priv->chip->smbus_master_fsm_off; - u32 timeout = MLXBF_I2C_SMBUS_TIMEOUT; - - if (mlxbf_i2c_poll(priv->mst->io, addr, mask, true, timeout)) - return true; - - return false; -} - -/* - * wait for the lock to be released before acquiring it. - */ -static bool mlxbf_i2c_smbus_master_lock(struct mlxbf_i2c_priv *priv) -{ - if (mlxbf_i2c_poll(priv->mst->io, MLXBF_I2C_SMBUS_MASTER_GW, - MLXBF_I2C_MASTER_LOCK_BIT, true, - MLXBF_I2C_SMBUS_LOCK_POLL_TIMEOUT)) - return true; - - return false; -} - -static void mlxbf_i2c_smbus_master_unlock(struct mlxbf_i2c_priv *priv) -{ - /* Clear the gw to clear the lock */ - writel(0, priv->mst->io + MLXBF_I2C_SMBUS_MASTER_GW); -} - static bool mlxbf_i2c_smbus_transaction_success(u32 master_status, u32 cause_status) { @@ -583,6 +525,7 @@ static int mlxbf_i2c_smbus_check_status(struct mlxbf_i2c_priv *priv) { u32 master_status_bits; u32 cause_status_bits; + u32 bits; /* * GW busy bit is raised by the driver and cleared by the HW @@ -591,9 +534,9 @@ static int mlxbf_i2c_smbus_check_status(struct mlxbf_i2c_priv *priv) * then read the cause and master status bits to determine if * errors occurred during the transaction. */ - mlxbf_i2c_poll(priv->mst->io, MLXBF_I2C_SMBUS_MASTER_GW, - MLXBF_I2C_MASTER_BUSY_BIT, true, - MLXBF_I2C_SMBUS_TIMEOUT); + readl_poll_timeout_atomic(priv->mst->io + MLXBF_I2C_SMBUS_MASTER_GW, + bits, !(bits & MLXBF_I2C_MASTER_BUSY_BIT), + MLXBF_I2C_POLL_FREQ_IN_USEC, MLXBF_I2C_SMBUS_TIMEOUT); /* Read cause status bits. */ cause_status_bits = readl(priv->mst_cause->io + @@ -740,7 +683,8 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv, u8 read_en, write_en, block_en, pec_en; u8 slave, flags, addr; u8 *read_buf; - int ret = 0; + u32 bits; + int ret; if (request->operation_cnt > MLXBF_I2C_SMBUS_MAX_OP_CNT) return -EINVAL; @@ -760,11 +704,22 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv, * Try to acquire the smbus gw lock before any reads of the GW register since * a read sets the lock. */ - if (WARN_ON(!mlxbf_i2c_smbus_master_lock(priv))) + ret = readl_poll_timeout_atomic(priv->mst->io + MLXBF_I2C_SMBUS_MASTER_GW, + bits, !(bits & MLXBF_I2C_MASTER_LOCK_BIT), + MLXBF_I2C_POLL_FREQ_IN_USEC, + MLXBF_I2C_SMBUS_LOCK_POLL_TIMEOUT); + if (WARN_ON(ret)) return -EBUSY; - /* Check whether the HW is idle */ - if (WARN_ON(!mlxbf_i2c_smbus_master_wait_for_idle(priv))) { + /* + * SW must make sure that the SMBus Master GW is idle before starting + * a transaction. Accordingly, this call polls the Master FSM stop bit; + * it returns -ETIMEDOUT when the bit is asserted, 0 if not. + */ + ret = readl_poll_timeout_atomic(priv->mst->io + priv->chip->smbus_master_fsm_off, + bits, !(bits & MLXBF_I2C_SMBUS_MASTER_FSM_STOP_MASK), + MLXBF_I2C_POLL_FREQ_IN_USEC, MLXBF_I2C_SMBUS_TIMEOUT); + if (WARN_ON(ret)) { ret = -EBUSY; goto out_unlock; } @@ -855,7 +810,8 @@ mlxbf_i2c_smbus_start_transaction(struct mlxbf_i2c_priv *priv, } out_unlock: - mlxbf_i2c_smbus_master_unlock(priv); + /* Clear the gw to clear the lock */ + writel(0, priv->mst->io + MLXBF_I2C_SMBUS_MASTER_GW); return ret; } @@ -1829,18 +1785,6 @@ static bool mlxbf_i2c_has_coalesce(struct mlxbf_i2c_priv *priv, bool *read, return true; } -static bool mlxbf_i2c_slave_wait_for_idle(struct mlxbf_i2c_priv *priv, - u32 timeout) -{ - u32 mask = MLXBF_I2C_CAUSE_S_GW_BUSY_FALL; - u32 addr = MLXBF_I2C_CAUSE_ARBITER; - - if (mlxbf_i2c_poll(priv->slv_cause->io, addr, mask, false, timeout)) - return true; - - return false; -} - static struct i2c_client *mlxbf_i2c_get_slave_from_addr( struct mlxbf_i2c_priv *priv, u8 addr) { @@ -1943,7 +1887,9 @@ static int mlxbf_i2c_irq_send(struct mlxbf_i2c_priv *priv, u8 recv_bytes) * Wait until the transfer is completed; the driver will wait * until the GW is idle, a cause will rise on fall of GW busy. */ - mlxbf_i2c_slave_wait_for_idle(priv, MLXBF_I2C_SMBUS_TIMEOUT); + readl_poll_timeout_atomic(priv->slv_cause->io + MLXBF_I2C_CAUSE_ARBITER, + data32, data32 & MLXBF_I2C_CAUSE_S_GW_BUSY_FALL, + MLXBF_I2C_POLL_FREQ_IN_USEC, MLXBF_I2C_SMBUS_TIMEOUT); clear_csr: /* Release the Slave GW. */ -- cgit v1.2.3-59-g8ed1b From a815975cbaeb4ab29f45312ef23be2871b2e8b82 Mon Sep 17 00:00:00 2001 From: Mukesh Kumar Savaliya Date: Wed, 22 Jan 2025 12:16:34 +0530 Subject: i2c: qcom-geni: Update i2c frequency table to match hardware guidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the current settings, the I2C buses are achieving around 370KHz instead of the expected 400KHz. For 100KHz and 1MHz, the settings are now more compliant and adhere to the Qualcomm’s internal programming guide. Update the I2C frequency table to align with the recommended values outlined in the I2C hardware programming guide, ensuring proper communication and performance. Signed-off-by: Mukesh Kumar Savaliya Reviewed-by: Vladimir Zapolskiy Link: https://lore.kernel.org/r/20250122064634.2864432-1-quic_msavaliy@quicinc.com Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-qcom-geni.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 7bbd478171e0..515a784c951c 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -148,9 +148,9 @@ struct geni_i2c_clk_fld { * source_clock = 19.2 MHz */ static const struct geni_i2c_clk_fld geni_i2c_clk_map_19p2mhz[] = { - {KHZ(100), 7, 10, 11, 26}, - {KHZ(400), 2, 5, 12, 24}, - {KHZ(1000), 1, 3, 9, 18}, + {KHZ(100), 7, 10, 12, 26}, + {KHZ(400), 2, 5, 11, 22}, + {KHZ(1000), 1, 2, 8, 18}, {}, }; -- cgit v1.2.3-59-g8ed1b From 39f8d63804505222dccf265797c2d03de7f2d5b3 Mon Sep 17 00:00:00 2001 From: Wentao Liang Date: Tue, 21 Jan 2025 16:48:18 +0800 Subject: i2c: iproc: Refactor prototype and remove redundant error checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bcm_iproc_i2c_init() always returns 0. As a result, there is no need to check its return value or handle errors. This patch changes the prototype of bcm_iproc_i2c_init() to return void and removes the redundant error handling code after calls to bcm_iproc_i2c_init() in both the bcm_iproc_i2c_probe() and bcm_iproc_i2c_resume(). Signed-off-by: Wentao Liang Acked-by: Uwe Kleine-König Acked-by: Ray Jui Link: https://lore.kernel.org/r/20250121084818.2719-1-vulab@iscas.ac.cn Signed-off-by: Andi Shyti --- drivers/i2c/busses/i2c-bcm-iproc.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index 15b632a146e1..332a0fcca28d 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -678,7 +678,7 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) return IRQ_HANDLED; } -static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) +static void bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) { u32 val; @@ -706,8 +706,6 @@ static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) /* clear all pending interrupts */ iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, 0xffffffff); - - return 0; } static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c, @@ -1081,9 +1079,7 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev) bcm_iproc_algo.unreg_slave = NULL; } - ret = bcm_iproc_i2c_init(iproc_i2c); - if (ret) - return ret; + bcm_iproc_i2c_init(iproc_i2c); ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); if (ret) @@ -1162,16 +1158,13 @@ static int bcm_iproc_i2c_suspend(struct device *dev) static int bcm_iproc_i2c_resume(struct device *dev) { struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev); - int ret; u32 val; /* * Power domain could have been shut off completely in system deep * sleep, so re-initialize the block here */ - ret = bcm_iproc_i2c_init(iproc_i2c); - if (ret) - return ret; + bcm_iproc_i2c_init(iproc_i2c); /* configure to the desired bus speed */ val = iproc_i2c_rd_reg(iproc_i2c, TIM_CFG_OFFSET); -- cgit v1.2.3-59-g8ed1b From 6b88dac0ae19fdb9124215f6ec3ca02944a237a4 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Wed, 19 Mar 2025 10:29:38 +0100 Subject: irqdomain: i2c: Switch to irq_find_mapping() irq_linear_revmap() is deprecated, so remove all its uses and supersede them by an identical call to irq_find_mapping(). Signed-off-by: Jiri Slaby (SUSE) Cc: Peter Rosin Cc: linux-i2c@vger.kernel.org Signed-off-by: Wolfram Sang --- drivers/i2c/muxes/i2c-mux-pca954x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c index 6f84018258c4..db95113a5b49 100644 --- a/drivers/i2c/muxes/i2c-mux-pca954x.c +++ b/drivers/i2c/muxes/i2c-mux-pca954x.c @@ -414,7 +414,7 @@ static irqreturn_t pca954x_irq_handler(int irq, void *dev_id) pending = (ret >> PCA954X_IRQ_OFFSET) & (BIT(data->chip->nchans) - 1); for_each_set_bit(i, &pending, data->chip->nchans) - handle_nested_irq(irq_linear_revmap(data->irq, i)); + handle_nested_irq(irq_find_mapping(data->irq, i)); return IRQ_RETVAL(pending); } -- cgit v1.2.3-59-g8ed1b