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

ucsi_ccg: Refine the UCSI Interrupt handling

With the Cypress CCGx Type-C controller the following error is
sometimes observed on boot:
[ 16.087147] ucsi_ccg 1-0008: failed to reset PPM!
[ 16.087319] ucsi_ccg 1-0008: PPM init failed (-110)

When the above timeout occurs the following happens:
1. The function ucsi_reset_ppm() is called to reset UCSI controller.
This function performs an async write to start reset and then
polls for completion.
2. An interrupt occurs when the reset completes. In the interrupt
handler, the OPM field in the INTR_REG is cleared and this clears
the CCI data in the PPM. Hence, the reset completion status is
cleared.
3. The function ucsi_reset_ppm() continues to poll for the reset
completion, but has missed the reset completion event and
eventually timeouts.

In this patch, we store CCI when handling the interrupt and make
reading after async write gets the correct value.

To align with the CCGx UCSI interface guide, this patch updates the
driver to copy CCI and MESSAGE_IN before they are reset when UCSI
interrupt acknowledged.

When a new command is sent, the driver will clear the old CCI to avoid
ucsi_ccg_read() getting wrong CCI after ucsi_ccg_async_write() when
the UCSI interrupt is not handled.

Finally, acking the UCSI_READ_INT interrupt before calling complete()
in ISR to ensure that the ucsi_ccg_sync_write() would wait for the
interrupt handling to complete.

Signed-off-by: Sing-Han Chen <singhanc@nvidia.com>
Signed-off-by: Haotien Hsu <haotienh@nvidia.com>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20240126030115.3791554-1-haotienh@nvidia.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Haotien Hsu and committed by
Greg Kroah-Hartman
05d039e1 4ca79255

+84 -8
+84 -8
drivers/usb/typec/ucsi/ucsi_ccg.c
··· 192 192 bool checked; 193 193 } __packed; 194 194 195 + #define CCGX_MESSAGE_IN_MAX 4 196 + struct op_region { 197 + __le32 cci; 198 + __le32 message_in[CCGX_MESSAGE_IN_MAX]; 199 + }; 200 + 195 201 struct ucsi_ccg { 196 202 struct device *dev; 197 203 struct ucsi *ucsi; ··· 228 222 bool has_multiple_dp; 229 223 struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES]; 230 224 struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES]; 225 + 226 + /* 227 + * This spinlock protects op_data which includes CCI and MESSAGE_IN that 228 + * will be updated in ISR 229 + */ 230 + spinlock_t op_lock; 231 + struct op_region op_data; 231 232 }; 232 233 233 234 static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len) ··· 318 305 return 0; 319 306 } 320 307 308 + static int ccg_op_region_update(struct ucsi_ccg *uc, u32 cci) 309 + { 310 + u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_MESSAGE_IN); 311 + struct op_region *data = &uc->op_data; 312 + unsigned char *buf; 313 + size_t size = sizeof(data->message_in); 314 + 315 + buf = kzalloc(size, GFP_ATOMIC); 316 + if (!buf) 317 + return -ENOMEM; 318 + if (UCSI_CCI_LENGTH(cci)) { 319 + int ret = ccg_read(uc, reg, (void *)buf, size); 320 + 321 + if (ret) { 322 + kfree(buf); 323 + return ret; 324 + } 325 + } 326 + 327 + spin_lock(&uc->op_lock); 328 + data->cci = cpu_to_le32(cci); 329 + if (UCSI_CCI_LENGTH(cci)) 330 + memcpy(&data->message_in, buf, size); 331 + spin_unlock(&uc->op_lock); 332 + kfree(buf); 333 + return 0; 334 + } 335 + 321 336 static int ucsi_ccg_init(struct ucsi_ccg *uc) 322 337 { 323 338 unsigned int count = 10; 324 339 u8 data; 325 340 int status; 341 + 342 + spin_lock_init(&uc->op_lock); 326 343 327 344 data = CCGX_RAB_UCSI_CONTROL_STOP; 328 345 status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data)); ··· 563 520 u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset); 564 521 struct ucsi_capability *cap; 565 522 struct ucsi_altmode *alt; 566 - int ret; 523 + int ret = 0; 567 524 568 - ret = ccg_read(uc, reg, val, val_len); 525 + if (offset == UCSI_CCI) { 526 + spin_lock(&uc->op_lock); 527 + memcpy(val, &(uc->op_data).cci, val_len); 528 + spin_unlock(&uc->op_lock); 529 + } else if (offset == UCSI_MESSAGE_IN) { 530 + spin_lock(&uc->op_lock); 531 + memcpy(val, &(uc->op_data).message_in, val_len); 532 + spin_unlock(&uc->op_lock); 533 + } else { 534 + ret = ccg_read(uc, reg, val, val_len); 535 + } 536 + 569 537 if (ret) 570 538 return ret; 571 539 ··· 613 559 static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset, 614 560 const void *val, size_t val_len) 615 561 { 562 + struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi); 616 563 u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset); 617 564 618 - return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len); 565 + /* 566 + * UCSI may read CCI instantly after async_write, 567 + * clear CCI to avoid caller getting wrong data before we get CCI from ISR 568 + */ 569 + spin_lock(&uc->op_lock); 570 + uc->op_data.cci = 0; 571 + spin_unlock(&uc->op_lock); 572 + 573 + return ccg_write(uc, reg, val, val_len); 619 574 } 620 575 621 576 static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset, ··· 678 615 u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CCI); 679 616 struct ucsi_ccg *uc = data; 680 617 u8 intr_reg; 681 - u32 cci; 682 - int ret; 618 + u32 cci = 0; 619 + int ret = 0; 683 620 684 621 ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg)); 685 622 if (ret) 686 623 return ret; 624 + 625 + if (!intr_reg) 626 + return IRQ_HANDLED; 627 + else if (!(intr_reg & UCSI_READ_INT)) 628 + goto err_clear_irq; 687 629 688 630 ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci)); 689 631 if (ret) ··· 697 629 if (UCSI_CCI_CONNECTOR(cci)) 698 630 ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci)); 699 631 700 - if (test_bit(DEV_CMD_PENDING, &uc->flags) && 701 - cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) 702 - complete(&uc->complete); 632 + /* 633 + * As per CCGx UCSI interface guide, copy CCI and MESSAGE_IN 634 + * to the OpRegion before clear the UCSI interrupt 635 + */ 636 + ret = ccg_op_region_update(uc, cci); 637 + if (ret) 638 + goto err_clear_irq; 703 639 704 640 err_clear_irq: 705 641 ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg)); 642 + 643 + if (!ret && test_bit(DEV_CMD_PENDING, &uc->flags) && 644 + cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) 645 + complete(&uc->complete); 706 646 707 647 return IRQ_HANDLED; 708 648 }