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.

i3c: mipi-i3c-hci: Fallback to software reset when bus disable fails

Disruption of the MIPI I3C HCI controller's internal state can cause
i3c_hci_bus_disable() to fail when attempting to shut down the bus.

In the code paths where bus disable is invoked - bus clean-up and runtime
suspend - the controller does not need to remain operational afterward, so
a full controller reset is a safe recovery mechanism.

Add a fallback to issue a software reset when disabling the bus fails.
This ensures the bus is reliably halted even if the controller's state
machine is stuck or unresponsive.

The fallback is used both during bus clean-up and in the runtime suspend
path. In the latter case, ensure interrupts are quiesced after reset.

Fixes: 9ad9a52cce282 ("i3c/master: introduce the mipi-i3c-hci driver")
Cc: stable@vger.kernel.org
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20260306072451.11131-15-adrian.hunter@intel.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Adrian Hunter and committed by
Alexandre Belloni
9a258d13 c6396b83

+35 -30
+35 -30
drivers/i3c/master/mipi-i3c-hci/core.c
··· 181 181 return ret; 182 182 } 183 183 184 + static int i3c_hci_software_reset(struct i3c_hci *hci) 185 + { 186 + u32 regval; 187 + int ret; 188 + 189 + /* 190 + * SOFT_RST must be clear before we write to it. 191 + * Then we must wait until it clears again. 192 + */ 193 + ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval, 194 + !(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC); 195 + if (ret) { 196 + dev_err(&hci->master.dev, "%s: Software reset stuck\n", __func__); 197 + return ret; 198 + } 199 + 200 + reg_write(RESET_CONTROL, SOFT_RST); 201 + 202 + ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval, 203 + !(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC); 204 + if (ret) { 205 + dev_err(&hci->master.dev, "%s: Software reset failed\n", __func__); 206 + return ret; 207 + } 208 + 209 + return 0; 210 + } 211 + 184 212 void i3c_hci_sync_irq_inactive(struct i3c_hci *hci) 185 213 { 186 214 struct platform_device *pdev = to_platform_device(hci->master.dev.parent); ··· 224 196 { 225 197 struct i3c_hci *hci = to_i3c_hci(m); 226 198 227 - i3c_hci_bus_disable(hci); 199 + if (i3c_hci_bus_disable(hci)) 200 + i3c_hci_software_reset(hci); 228 201 hci->io->cleanup(hci); 229 202 } 230 203 ··· 655 626 return result; 656 627 } 657 628 658 - static int i3c_hci_software_reset(struct i3c_hci *hci) 659 - { 660 - u32 regval; 661 - int ret; 662 - 663 - /* 664 - * SOFT_RST must be clear before we write to it. 665 - * Then we must wait until it clears again. 666 - */ 667 - ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval, 668 - !(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC); 669 - if (ret) { 670 - dev_err(&hci->master.dev, "%s: Software reset stuck\n", __func__); 671 - return ret; 672 - } 673 - 674 - reg_write(RESET_CONTROL, SOFT_RST); 675 - 676 - ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval, 677 - !(regval & SOFT_RST), 0, 10 * USEC_PER_MSEC); 678 - if (ret) { 679 - dev_err(&hci->master.dev, "%s: Software reset failed\n", __func__); 680 - return ret; 681 - } 682 - 683 - return 0; 684 - } 685 - 686 629 static inline bool is_version_1_1_or_newer(struct i3c_hci *hci) 687 630 { 688 631 return hci->version_major > 1 || (hci->version_major == 1 && hci->version_minor > 0); ··· 765 764 int ret; 766 765 767 766 ret = i3c_hci_bus_disable(hci); 768 - if (ret) 767 + if (ret) { 768 + /* Fall back to software reset to disable the bus */ 769 + ret = i3c_hci_software_reset(hci); 770 + i3c_hci_sync_irq_inactive(hci); 769 771 return ret; 772 + } 770 773 771 774 hci->io->suspend(hci); 772 775