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

Configure Feed

Select the types of activity you want to include in your feed.

i2c: designware: Implement I2C_M_STOP support

Add the support of the I2C_M_STOP flag in i2c_msg by splitting
i2c_dw_xfer() in two: __i2c_dw_xfer_one_part() for the core transfer logic
and i2c_dw_xfer() for handling the high-level transaction management.

In detail __i2c_dw_xfer_one_part() starts a transaction and wait for its
completion, either with a STOP on the bus or an error. i2c_dw_xfer()
loops over the messages to search for the I2C_M_STOP flag and calls
__i2c_dw_xfer_one_part() for each part of the messages up to a STOP or
the end of the messages array.

i2c_dw_xfer() takes care of runtime PM and holds the hardware lock on
the bus while calling __i2c_dw_xfer_one_part(), this allows grouping
multiple accesses to device that support a STOP in a transaction when
done via i2c_dev I2C_RDWR ioctl.

Also, now that we have a lookup of the messages in i2c_dw_xfer() prior
to each transaction, we use it to make sure the messages are valid for
the transaction, via a new function i2c_dw_msg_is_valid(). We check
that the target address does not change before starting the transaction
instead of aborting the transfer while it is happening, as it was done
in i2c_dw_xfer_msg(). The target address can only be changed after an
I2C_M_STOP flag, i.e after a STOP on the i2c bus.

The I2C_FUNC_PROTOCOL_MANGLING flag is added to the list of
functionalities supported by the controller, except for the AMD NAVI
i2c controller which uses its own xfer() function and is left untouched.

Signed-off-by: Benoît Monin <benoit.monin@bootlin.com>
Acked-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Link: https://lore.kernel.org/r/20260130-i2c-dw-v6-1-08ca1e9ece07@bootlin.com

authored by

Benoît Monin and committed by
Andi Shyti
470f1a71 51e8ce36

+93 -39
+93 -39
drivers/i2c/busses/i2c-designware-master.c
··· 377 377 struct i2c_msg *msgs = dev->msgs; 378 378 u32 intr_mask; 379 379 int tx_limit, rx_limit; 380 - u32 addr = msgs[dev->msg_write_idx].addr; 381 380 u32 buf_len = dev->tx_buf_len; 382 381 u8 *buf = dev->tx_buf; 383 382 bool need_restart = false; ··· 386 387 387 388 for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) { 388 389 u32 flags = msgs[dev->msg_write_idx].flags; 389 - 390 - /* 391 - * If target address has changed, we need to 392 - * reprogram the target address in the I2C 393 - * adapter when we are done with this transfer. 394 - */ 395 - if (msgs[dev->msg_write_idx].addr != addr) { 396 - dev_err(dev->dev, 397 - "%s: invalid target address\n", __func__); 398 - dev->msg_err = -EINVAL; 399 - break; 400 - } 401 390 402 391 if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { 403 392 /* new i2c_msg */ ··· 733 746 } 734 747 735 748 /* 736 - * Prepare controller for a transaction and call i2c_dw_xfer_msg. 749 + * Prepare controller for a transaction, start the transfer of the @msgs 750 + * and wait for completion, either a STOP or a error. 751 + * Return: 0 or a negative error code. 737 752 */ 738 753 static int 739 - i2c_dw_xfer_common(struct dw_i2c_dev *dev, struct i2c_msg msgs[], int num) 754 + __i2c_dw_xfer_one_part(struct dw_i2c_dev *dev, struct i2c_msg *msgs, size_t num) 740 755 { 741 756 int ret; 742 - 743 - dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); 744 - 745 - pm_runtime_get_sync(dev->dev); 746 757 747 758 reinit_completion(&dev->cmd_complete); 748 759 dev->msgs = msgs; ··· 753 768 dev->abort_source = 0; 754 769 dev->rx_outstanding = 0; 755 770 756 - ret = i2c_dw_acquire_lock(dev); 757 - if (ret) 758 - goto done_nolock; 759 - 760 771 ret = i2c_dw_wait_bus_not_busy(dev); 761 772 if (ret < 0) 762 - goto done; 773 + return ret; 763 774 764 775 /* Start the transfers */ 765 776 i2c_dw_xfer_init(dev); ··· 767 786 /* i2c_dw_init() implicitly disables the adapter */ 768 787 i2c_recover_bus(&dev->adapter); 769 788 i2c_dw_init(dev); 770 - goto done; 789 + return ret; 771 790 } 772 791 773 792 /* ··· 790 809 */ 791 810 __i2c_dw_disable_nowait(dev); 792 811 793 - if (dev->msg_err) { 794 - ret = dev->msg_err; 795 - goto done; 796 - } 812 + if (dev->msg_err) 813 + return dev->msg_err; 797 814 798 815 /* No error */ 799 - if (likely(!dev->cmd_err && !dev->status)) { 800 - ret = num; 801 - goto done; 802 - } 816 + if (likely(!dev->cmd_err && !dev->status)) 817 + return 0; 803 818 804 819 /* We have an error */ 805 - if (dev->cmd_err == DW_IC_ERR_TX_ABRT) { 806 - ret = i2c_dw_handle_tx_abort(dev); 807 - goto done; 808 - } 820 + if (dev->cmd_err == DW_IC_ERR_TX_ABRT) 821 + return i2c_dw_handle_tx_abort(dev); 809 822 810 823 if (dev->status) 811 824 dev_err(dev->dev, 812 825 "transfer terminated early - interrupt latency too high?\n"); 813 826 814 - ret = -EIO; 827 + return -EIO; 828 + } 829 + 830 + /* 831 + * Verify that the message at index @idx can be processed as part 832 + * of a single transaction. The @msgs array contains the messages 833 + * of the transaction. The message is checked against its predecessor 834 + * to ensure that it respects the limitation of the controller. 835 + * Return: true if the message can be processed, false otherwise. 836 + */ 837 + static bool 838 + i2c_dw_msg_is_valid(struct dw_i2c_dev *dev, const struct i2c_msg *msgs, size_t idx) 839 + { 840 + /* 841 + * The first message of a transaction is valid, 842 + * no constraints from a previous message. 843 + */ 844 + if (!idx) 845 + return true; 846 + 847 + /* 848 + * We cannot change the target address during a transaction, so make 849 + * sure the address is identical to the one of the previous message. 850 + */ 851 + if (msgs[idx - 1].addr != msgs[idx].addr) { 852 + dev_err(dev->dev, "invalid target address\n"); 853 + return false; 854 + } 855 + 856 + return true; 857 + } 858 + 859 + static int 860 + i2c_dw_xfer_common(struct dw_i2c_dev *dev, struct i2c_msg msgs[], int num) 861 + { 862 + struct i2c_msg *msgs_part; 863 + size_t cnt; 864 + int ret; 865 + 866 + dev_dbg(dev->dev, "msgs: %d\n", num); 867 + 868 + pm_runtime_get_sync(dev->dev); 869 + 870 + ret = i2c_dw_acquire_lock(dev); 871 + if (ret) 872 + goto done_nolock; 873 + 874 + /* 875 + * If the I2C_M_STOP is present in some the messages, 876 + * we do one transaction for each part up to the STOP. 877 + */ 878 + for (msgs_part = msgs; msgs_part < msgs + num; msgs_part += cnt) { 879 + /* 880 + * Count the messages in a transaction, up to a STOP or 881 + * the end of the msgs. The last if below guarantees that 882 + * we check all messages and that msg_parts and cnt are 883 + * in-bounds of msgs and num. 884 + */ 885 + for (cnt = 1; ; cnt++) { 886 + if (!i2c_dw_msg_is_valid(dev, msgs_part, cnt - 1)) { 887 + ret = -EINVAL; 888 + goto done; 889 + } 890 + 891 + if ((msgs_part[cnt - 1].flags & I2C_M_STOP) || 892 + (msgs_part + cnt == msgs + num)) 893 + break; 894 + } 895 + 896 + /* transfer one part up to a STOP */ 897 + ret = __i2c_dw_xfer_one_part(dev, msgs_part, cnt); 898 + if (ret < 0) 899 + break; 900 + } 815 901 816 902 done: 817 903 i2c_dw_set_mode(dev, DW_IC_SLAVE); ··· 888 840 done_nolock: 889 841 pm_runtime_put_autosuspend(dev->dev); 890 842 891 - return ret; 843 + if (ret < 0) 844 + return ret; 845 + return num; 892 846 } 893 847 894 848 int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ··· 908 858 struct i2c_timings *t = &dev->timings; 909 859 910 860 dev->functionality |= I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY; 861 + 862 + /* amd_i2c_dw_xfer_quirk() does not implement protocol mangling */ 863 + if ((dev->flags & MODEL_MASK) != MODEL_AMD_NAVI_GPU) 864 + dev->functionality |= I2C_FUNC_PROTOCOL_MANGLING; 911 865 912 866 dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | 913 867 DW_IC_CON_RESTART_EN;