| @@ -108,6 +108,12 @@ · 107 unmodified lines |
| 108 | 108 | #define ASPEED_I2CD_S_TX_CMD BIT(2) |
| 109 | 109 | #define ASPEED_I2CD_M_TX_CMD BIT(1) |
| 110 | 110 | #define ASPEED_I2CD_M_START_CMD BIT(0) |
| 111 | +#define ASPEED_I2CD_MASTER_CMDS_MASK \ |
| 112 | + (ASPEED_I2CD_M_STOP_CMD | \ |
| 113 | + ASPEED_I2CD_M_S_RX_CMD_LAST | \ |
| 114 | + ASPEED_I2CD_M_RX_CMD | \ |
| 115 | + ASPEED_I2CD_M_TX_CMD | \ |
| 116 | + ASPEED_I2CD_M_START_CMD) |
| 111 | 117 | |
| 112 | 118 | /* 0x18 : I2CD Slave Device Address Register */ |
| 113 | 119 | #define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0) |
| @@ -336,18 +342,19 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) · 222 unmodified lines |
| 336 | 342 | struct i2c_msg *msg = &bus->msgs[bus->msgs_index]; |
| 337 | 343 | u8 slave_addr = i2c_8bit_addr_from_msg(msg); |
| 338 | 344 | |
| 339 | | − bus->master_state = ASPEED_I2C_MASTER_START; |
| 340 | | − |
| 341 | 345 | #if IS_ENABLED(CONFIG_I2C_SLAVE) |
| 342 | 346 | /* |
| 343 | 347 | * If it's requested in the middle of a slave session, set the master |
| 344 | 348 | * state to 'pending' then H/W will continue handling this master |
| 345 | 349 | * command when the bus comes back to the idle state. |
| 346 | 350 | */ |
| 347 | | − if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) |
| 351 | + if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) { |
| 348 | 352 | bus->master_state = ASPEED_I2C_MASTER_PENDING; |
| 353 | + return; |
| 354 | + } |
| 349 | 355 | #endif /* CONFIG_I2C_SLAVE */ |
| 350 | 356 | |
| 357 | + bus->master_state = ASPEED_I2C_MASTER_START; |
| 351 | 358 | bus->buf_index = 0; |
| 352 | 359 | |
| 353 | 360 | if (msg->flags & I2C_M_RD) { |
| @@ -422,20 +429,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) · 68 unmodified lines |
| 422 | 429 | } |
| 423 | 430 | } |
| 424 | 431 | |
| 425 | | −#if IS_ENABLED(CONFIG_I2C_SLAVE) |
| 426 | | − /* |
| 427 | | − * A pending master command will be started by H/W when the bus comes |
| 428 | | − * back to idle state after completing a slave operation so change the |
| 429 | | − * master state from 'pending' to 'start' at here if slave is inactive. |
| 430 | | − */ |
| 431 | | − if (bus->master_state == ASPEED_I2C_MASTER_PENDING) { |
| 432 | | − if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) |
| 433 | | − goto out_no_complete; |
| 434 | | − |
| 435 | | − bus->master_state = ASPEED_I2C_MASTER_START; |
| 436 | | − } |
| 437 | | −#endif /* CONFIG_I2C_SLAVE */ |
| 438 | | − |
| 439 | 432 | /* Master is not currently active, irq was for someone else. */ |
| 440 | 433 | if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || |
| 441 | 434 | bus->master_state == ASPEED_I2C_MASTER_PENDING) |
| @@ -462,11 +455,15 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) · 20 unmodified lines |
| 462 | 455 | #if IS_ENABLED(CONFIG_I2C_SLAVE) |
| 463 | 456 | /* |
| 464 | 457 | * If a peer master starts a xfer immediately after it queues a |
| 465 | | − * master command, change its state to 'pending' then H/W will |
| 466 | | − * continue the queued master xfer just after completing the |
| 467 | | − * slave mode session. |
| 458 | + * master command, clear the queued master command and change |
| 459 | + * its state to 'pending'. To simplify handling of pending |
| 460 | + * cases, it uses S/W solution instead of H/W command queue |
| 461 | + * handling. |
| 468 | 462 | */ |
| 469 | 463 | if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { |
| 464 | + writel(readl(bus->base + ASPEED_I2C_CMD_REG) & |
| 465 | + ~ASPEED_I2CD_MASTER_CMDS_MASK, |
| 466 | + bus->base + ASPEED_I2C_CMD_REG); |
| 470 | 467 | bus->master_state = ASPEED_I2C_MASTER_PENDING; |
| 471 | 468 | dev_dbg(bus->dev, |
| 472 | 469 | "master goes pending due to a slave start\n"); |
| @@ -629,6 +626,14 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) · 156 unmodified lines |
| 629 | 626 | irq_handled |= aspeed_i2c_master_irq(bus, |
| 630 | 627 | irq_remaining); |
| 631 | 628 | } |
| 629 | + |
| 630 | + /* |
| 631 | + * Start a pending master command at here if a slave operation is |
| 632 | + * completed. |
| 633 | + */ |
| 634 | + if (bus->master_state == ASPEED_I2C_MASTER_PENDING && |
| 635 | + bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) |
| 636 | + aspeed_i2c_do_start(bus); |
| 632 | 637 | #else |
| 633 | 638 | irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); |
| 634 | 639 | #endif /* CONFIG_I2C_SLAVE */ |
| @@ -691,6 +696,15 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, · 56 unmodified lines |
| 691 | 696 | ASPEED_I2CD_BUS_BUSY_STS)) |
| 692 | 697 | aspeed_i2c_recover_bus(bus); |
| 693 | 698 | |
| 699 | + /* |
| 700 | + * If timed out and the state is still pending, drop the pending |
| 701 | + * master command. |
| 702 | + */ |
| 703 | + spin_lock_irqsave(&bus->lock, flags); |
| 704 | + if (bus->master_state == ASPEED_I2C_MASTER_PENDING) |
| 705 | + bus->master_state = ASPEED_I2C_MASTER_INACTIVE; |
| 706 | + spin_unlock_irqrestore(&bus->lock, flags); |
| 707 | + |
| 694 | 708 | return -ETIMEDOUT; |
| 695 | 709 | } |
| 696 | 710 | |