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

i2c: aspeed: Add multi-master use case support

In multi-master environment, this driver's master cannot know
exactly when a peer master sends data to this driver's slave so
cases can be happened that this master tries sending data through
the master_xfer function but slave data from a peer master is still
being processed or slave xfer is started by a peer immediately
after it queues a master command. To support multi-master use cases
properly, this H/W provides arbitration in physical level and it
provides priority based command handling too to avoid conflicts in
multi-master environment, means that if a master and a slave events
happen at the same time, H/W will handle a higher priority event
first and a pending event will be handled when bus comes back to
the idle state.

To support this H/W feature properly, this patch adds the 'pending'
state of master and its handling code so that the pending master
xfer can be continued after slave operation properly.

Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>

authored by

Jae Hyun Yoo and committed by
Wolfram Sang
2e57b7ce bceb26bf

+93 -26
+93 -26
drivers/i2c/busses/i2c-aspeed.c
··· 117 117 118 118 enum aspeed_i2c_master_state { 119 119 ASPEED_I2C_MASTER_INACTIVE, 120 + ASPEED_I2C_MASTER_PENDING, 120 121 ASPEED_I2C_MASTER_START, 121 122 ASPEED_I2C_MASTER_TX_FIRST, 122 123 ASPEED_I2C_MASTER_TX, ··· 127 126 }; 128 127 129 128 enum aspeed_i2c_slave_state { 130 - ASPEED_I2C_SLAVE_STOP, 129 + ASPEED_I2C_SLAVE_INACTIVE, 131 130 ASPEED_I2C_SLAVE_START, 132 131 ASPEED_I2C_SLAVE_READ_REQUESTED, 133 132 ASPEED_I2C_SLAVE_READ_PROCESSED, 134 133 ASPEED_I2C_SLAVE_WRITE_REQUESTED, 135 134 ASPEED_I2C_SLAVE_WRITE_RECEIVED, 135 + ASPEED_I2C_SLAVE_STOP, 136 136 }; 137 137 138 138 struct aspeed_i2c_bus { ··· 158 156 int cmd_err; 159 157 /* Protected only by i2c_lock_bus */ 160 158 int master_xfer_result; 159 + /* Multi-master */ 160 + bool multi_master; 161 161 #if IS_ENABLED(CONFIG_I2C_SLAVE) 162 162 struct i2c_client *slave; 163 163 enum aspeed_i2c_slave_state slave_state; ··· 255 251 } 256 252 257 253 /* Slave is not currently active, irq was for someone else. */ 258 - if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) 254 + if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) 259 255 return irq_handled; 260 256 261 257 dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", ··· 281 277 irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; 282 278 bus->slave_state = ASPEED_I2C_SLAVE_STOP; 283 279 } 284 - if (irq_status & ASPEED_I2CD_INTR_TX_NAK) { 280 + if (irq_status & ASPEED_I2CD_INTR_TX_NAK && 281 + bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { 285 282 irq_handled |= ASPEED_I2CD_INTR_TX_NAK; 286 283 bus->slave_state = ASPEED_I2C_SLAVE_STOP; 287 284 } 288 - if (irq_status & ASPEED_I2CD_INTR_TX_ACK) 289 - irq_handled |= ASPEED_I2CD_INTR_TX_ACK; 290 285 291 286 switch (bus->slave_state) { 292 287 case ASPEED_I2C_SLAVE_READ_REQUESTED: 293 - if (irq_status & ASPEED_I2CD_INTR_TX_ACK) 288 + if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) 294 289 dev_err(bus->dev, "Unexpected ACK on read request.\n"); 295 290 bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; 296 291 i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); ··· 297 294 writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); 298 295 break; 299 296 case ASPEED_I2C_SLAVE_READ_PROCESSED: 300 - if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK)) 297 + if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { 301 298 dev_err(bus->dev, 302 299 "Expected ACK after processed read.\n"); 300 + break; 301 + } 302 + irq_handled |= ASPEED_I2CD_INTR_TX_ACK; 303 303 i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); 304 304 writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); 305 305 writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); ··· 316 310 break; 317 311 case ASPEED_I2C_SLAVE_STOP: 318 312 i2c_slave_event(slave, I2C_SLAVE_STOP, &value); 313 + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; 314 + break; 315 + case ASPEED_I2C_SLAVE_START: 316 + /* Slave was just started. Waiting for the next event. */; 319 317 break; 320 318 default: 321 - dev_err(bus->dev, "unhandled slave_state: %d\n", 319 + dev_err(bus->dev, "unknown slave_state: %d\n", 322 320 bus->slave_state); 321 + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; 323 322 break; 324 323 } 325 324 ··· 340 329 u8 slave_addr = i2c_8bit_addr_from_msg(msg); 341 330 342 331 bus->master_state = ASPEED_I2C_MASTER_START; 332 + 333 + #if IS_ENABLED(CONFIG_I2C_SLAVE) 334 + /* 335 + * If it's requested in the middle of a slave session, set the master 336 + * state to 'pending' then H/W will continue handling this master 337 + * command when the bus comes back to the idle state. 338 + */ 339 + if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) 340 + bus->master_state = ASPEED_I2C_MASTER_PENDING; 341 + #endif /* CONFIG_I2C_SLAVE */ 342 + 343 343 bus->buf_index = 0; 344 344 345 345 if (msg->flags & I2C_M_RD) { ··· 406 384 bus->master_state = ASPEED_I2C_MASTER_INACTIVE; 407 385 irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; 408 386 goto out_complete; 409 - } else { 410 - /* Master is not currently active, irq was for someone else. */ 411 - if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE) 412 - goto out_no_complete; 413 387 } 414 388 415 389 /* ··· 417 399 if (ret) { 418 400 dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", 419 401 irq_status); 420 - bus->cmd_err = ret; 421 - bus->master_state = ASPEED_I2C_MASTER_INACTIVE; 422 402 irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); 423 - goto out_complete; 403 + if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { 404 + bus->cmd_err = ret; 405 + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; 406 + goto out_complete; 407 + } 424 408 } 409 + 410 + #if IS_ENABLED(CONFIG_I2C_SLAVE) 411 + /* 412 + * A pending master command will be started by H/W when the bus comes 413 + * back to idle state after completing a slave operation so change the 414 + * master state from 'pending' to 'start' at here if slave is inactive. 415 + */ 416 + if (bus->master_state == ASPEED_I2C_MASTER_PENDING) { 417 + if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) 418 + goto out_no_complete; 419 + 420 + bus->master_state = ASPEED_I2C_MASTER_START; 421 + } 422 + #endif /* CONFIG_I2C_SLAVE */ 423 + 424 + /* Master is not currently active, irq was for someone else. */ 425 + if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || 426 + bus->master_state == ASPEED_I2C_MASTER_PENDING) 427 + goto out_no_complete; 425 428 426 429 /* We are in an invalid state; reset bus to a known state. */ 427 430 if (!bus->msgs) { ··· 462 423 * then update the state and handle the new state below. 463 424 */ 464 425 if (bus->master_state == ASPEED_I2C_MASTER_START) { 426 + #if IS_ENABLED(CONFIG_I2C_SLAVE) 427 + /* 428 + * If a peer master starts a xfer immediately after it queues a 429 + * master command, change its state to 'pending' then H/W will 430 + * continue the queued master xfer just after completing the 431 + * slave mode session. 432 + */ 433 + if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { 434 + bus->master_state = ASPEED_I2C_MASTER_PENDING; 435 + dev_dbg(bus->dev, 436 + "master goes pending due to a slave start\n"); 437 + goto out_no_complete; 438 + } 439 + #endif /* CONFIG_I2C_SLAVE */ 465 440 if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { 466 441 if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { 467 442 bus->cmd_err = -ENXIO; ··· 619 566 * interrupt bits. Each case needs to be handled using corresponding 620 567 * handlers depending on the current state. 621 568 */ 622 - if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { 569 + if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE && 570 + bus->master_state != ASPEED_I2C_MASTER_PENDING) { 623 571 irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); 624 572 irq_remaining &= ~irq_handled; 625 573 if (irq_remaining) ··· 655 601 { 656 602 struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); 657 603 unsigned long time_left, flags; 658 - int ret = 0; 659 604 660 605 spin_lock_irqsave(&bus->lock, flags); 661 606 bus->cmd_err = 0; 662 607 663 - /* If bus is busy, attempt recovery. We assume a single master 664 - * environment. 665 - */ 666 - if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) { 608 + /* If bus is busy in a single master environment, attempt recovery. */ 609 + if (!bus->multi_master && 610 + (readl(bus->base + ASPEED_I2C_CMD_REG) & 611 + ASPEED_I2CD_BUS_BUSY_STS)) { 612 + int ret; 613 + 667 614 spin_unlock_irqrestore(&bus->lock, flags); 668 615 ret = aspeed_i2c_recover_bus(bus); 669 616 if (ret) ··· 684 629 time_left = wait_for_completion_timeout(&bus->cmd_complete, 685 630 bus->adap.timeout); 686 631 687 - if (time_left == 0) 632 + if (time_left == 0) { 633 + /* 634 + * If timed out and bus is still busy in a multi master 635 + * environment, attempt recovery at here. 636 + */ 637 + if (bus->multi_master && 638 + (readl(bus->base + ASPEED_I2C_CMD_REG) & 639 + ASPEED_I2CD_BUS_BUSY_STS)) 640 + aspeed_i2c_recover_bus(bus); 641 + 688 642 return -ETIMEDOUT; 689 - else 690 - return bus->master_xfer_result; 643 + } 644 + 645 + return bus->master_xfer_result; 691 646 } 692 647 693 648 static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) ··· 737 672 __aspeed_i2c_reg_slave(bus, client->addr); 738 673 739 674 bus->slave = client; 740 - bus->slave_state = ASPEED_I2C_SLAVE_STOP; 675 + bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; 741 676 spin_unlock_irqrestore(&bus->lock, flags); 742 677 743 678 return 0; ··· 892 827 if (ret < 0) 893 828 return ret; 894 829 895 - if (!of_property_read_bool(pdev->dev.of_node, "multi-master")) 830 + if (of_property_read_bool(pdev->dev.of_node, "multi-master")) 831 + bus->multi_master = true; 832 + else 896 833 fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; 897 834 898 835 /* Enable Master Mode */