diff options
author | Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com> | 2019-02-11 13:54:44 -0500 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2019-02-15 03:52:03 -0500 |
commit | 2e57b7cebb988a27cee44626ae91424e73823bfb (patch) | |
tree | 42f8f722e9b1d40aef77983e54b0b6d1ff7bbaf8 /drivers/i2c | |
parent | bceb26bffebf4c8f167787eb87b6a858d991e48e (diff) |
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>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-aspeed.c | 119 |
1 files changed, 93 insertions, 26 deletions
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 833b6b6a4c7e..6c8b38fd6e64 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c | |||
@@ -117,6 +117,7 @@ | |||
117 | 117 | ||
118 | enum aspeed_i2c_master_state { | 118 | enum aspeed_i2c_master_state { |
119 | ASPEED_I2C_MASTER_INACTIVE, | 119 | ASPEED_I2C_MASTER_INACTIVE, |
120 | ASPEED_I2C_MASTER_PENDING, | ||
120 | ASPEED_I2C_MASTER_START, | 121 | ASPEED_I2C_MASTER_START, |
121 | ASPEED_I2C_MASTER_TX_FIRST, | 122 | ASPEED_I2C_MASTER_TX_FIRST, |
122 | ASPEED_I2C_MASTER_TX, | 123 | ASPEED_I2C_MASTER_TX, |
@@ -126,12 +127,13 @@ enum aspeed_i2c_master_state { | |||
126 | }; | 127 | }; |
127 | 128 | ||
128 | enum aspeed_i2c_slave_state { | 129 | enum aspeed_i2c_slave_state { |
129 | ASPEED_I2C_SLAVE_STOP, | 130 | ASPEED_I2C_SLAVE_INACTIVE, |
130 | ASPEED_I2C_SLAVE_START, | 131 | ASPEED_I2C_SLAVE_START, |
131 | ASPEED_I2C_SLAVE_READ_REQUESTED, | 132 | ASPEED_I2C_SLAVE_READ_REQUESTED, |
132 | ASPEED_I2C_SLAVE_READ_PROCESSED, | 133 | ASPEED_I2C_SLAVE_READ_PROCESSED, |
133 | ASPEED_I2C_SLAVE_WRITE_REQUESTED, | 134 | ASPEED_I2C_SLAVE_WRITE_REQUESTED, |
134 | ASPEED_I2C_SLAVE_WRITE_RECEIVED, | 135 | ASPEED_I2C_SLAVE_WRITE_RECEIVED, |
136 | ASPEED_I2C_SLAVE_STOP, | ||
135 | }; | 137 | }; |
136 | 138 | ||
137 | struct aspeed_i2c_bus { | 139 | struct aspeed_i2c_bus { |
@@ -156,6 +158,8 @@ struct aspeed_i2c_bus { | |||
156 | int cmd_err; | 158 | int cmd_err; |
157 | /* Protected only by i2c_lock_bus */ | 159 | /* Protected only by i2c_lock_bus */ |
158 | int master_xfer_result; | 160 | int master_xfer_result; |
161 | /* Multi-master */ | ||
162 | bool multi_master; | ||
159 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | 163 | #if IS_ENABLED(CONFIG_I2C_SLAVE) |
160 | struct i2c_client *slave; | 164 | struct i2c_client *slave; |
161 | enum aspeed_i2c_slave_state slave_state; | 165 | enum aspeed_i2c_slave_state slave_state; |
@@ -251,7 +255,7 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) | |||
251 | } | 255 | } |
252 | 256 | ||
253 | /* Slave is not currently active, irq was for someone else. */ | 257 | /* Slave is not currently active, irq was for someone else. */ |
254 | if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) | 258 | if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE) |
255 | return irq_handled; | 259 | return irq_handled; |
256 | 260 | ||
257 | dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", | 261 | dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", |
@@ -277,16 +281,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) | |||
277 | irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; | 281 | irq_handled |= ASPEED_I2CD_INTR_NORMAL_STOP; |
278 | bus->slave_state = ASPEED_I2C_SLAVE_STOP; | 282 | bus->slave_state = ASPEED_I2C_SLAVE_STOP; |
279 | } | 283 | } |
280 | if (irq_status & ASPEED_I2CD_INTR_TX_NAK) { | 284 | if (irq_status & ASPEED_I2CD_INTR_TX_NAK && |
285 | bus->slave_state == ASPEED_I2C_SLAVE_READ_PROCESSED) { | ||
281 | irq_handled |= ASPEED_I2CD_INTR_TX_NAK; | 286 | irq_handled |= ASPEED_I2CD_INTR_TX_NAK; |
282 | bus->slave_state = ASPEED_I2C_SLAVE_STOP; | 287 | bus->slave_state = ASPEED_I2C_SLAVE_STOP; |
283 | } | 288 | } |
284 | if (irq_status & ASPEED_I2CD_INTR_TX_ACK) | ||
285 | irq_handled |= ASPEED_I2CD_INTR_TX_ACK; | ||
286 | 289 | ||
287 | switch (bus->slave_state) { | 290 | switch (bus->slave_state) { |
288 | case ASPEED_I2C_SLAVE_READ_REQUESTED: | 291 | case ASPEED_I2C_SLAVE_READ_REQUESTED: |
289 | if (irq_status & ASPEED_I2CD_INTR_TX_ACK) | 292 | if (unlikely(irq_status & ASPEED_I2CD_INTR_TX_ACK)) |
290 | dev_err(bus->dev, "Unexpected ACK on read request.\n"); | 293 | dev_err(bus->dev, "Unexpected ACK on read request.\n"); |
291 | bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; | 294 | bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; |
292 | i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); | 295 | i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); |
@@ -294,9 +297,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) | |||
294 | writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); | 297 | writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); |
295 | break; | 298 | break; |
296 | case ASPEED_I2C_SLAVE_READ_PROCESSED: | 299 | case ASPEED_I2C_SLAVE_READ_PROCESSED: |
297 | if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK)) | 300 | if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { |
298 | dev_err(bus->dev, | 301 | dev_err(bus->dev, |
299 | "Expected ACK after processed read.\n"); | 302 | "Expected ACK after processed read.\n"); |
303 | break; | ||
304 | } | ||
305 | irq_handled |= ASPEED_I2CD_INTR_TX_ACK; | ||
300 | i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); | 306 | i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); |
301 | writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); | 307 | writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); |
302 | writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); | 308 | writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); |
@@ -310,10 +316,15 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status) | |||
310 | break; | 316 | break; |
311 | case ASPEED_I2C_SLAVE_STOP: | 317 | case ASPEED_I2C_SLAVE_STOP: |
312 | i2c_slave_event(slave, I2C_SLAVE_STOP, &value); | 318 | i2c_slave_event(slave, I2C_SLAVE_STOP, &value); |
319 | bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; | ||
320 | break; | ||
321 | case ASPEED_I2C_SLAVE_START: | ||
322 | /* Slave was just started. Waiting for the next event. */; | ||
313 | break; | 323 | break; |
314 | default: | 324 | default: |
315 | dev_err(bus->dev, "unhandled slave_state: %d\n", | 325 | dev_err(bus->dev, "unknown slave_state: %d\n", |
316 | bus->slave_state); | 326 | bus->slave_state); |
327 | bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; | ||
317 | break; | 328 | break; |
318 | } | 329 | } |
319 | 330 | ||
@@ -329,6 +340,17 @@ static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) | |||
329 | u8 slave_addr = i2c_8bit_addr_from_msg(msg); | 340 | u8 slave_addr = i2c_8bit_addr_from_msg(msg); |
330 | 341 | ||
331 | bus->master_state = ASPEED_I2C_MASTER_START; | 342 | bus->master_state = ASPEED_I2C_MASTER_START; |
343 | |||
344 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | ||
345 | /* | ||
346 | * If it's requested in the middle of a slave session, set the master | ||
347 | * state to 'pending' then H/W will continue handling this master | ||
348 | * command when the bus comes back to the idle state. | ||
349 | */ | ||
350 | if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) | ||
351 | bus->master_state = ASPEED_I2C_MASTER_PENDING; | ||
352 | #endif /* CONFIG_I2C_SLAVE */ | ||
353 | |||
332 | bus->buf_index = 0; | 354 | bus->buf_index = 0; |
333 | 355 | ||
334 | if (msg->flags & I2C_M_RD) { | 356 | if (msg->flags & I2C_M_RD) { |
@@ -384,10 +406,6 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) | |||
384 | bus->master_state = ASPEED_I2C_MASTER_INACTIVE; | 406 | bus->master_state = ASPEED_I2C_MASTER_INACTIVE; |
385 | irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; | 407 | irq_handled |= ASPEED_I2CD_INTR_BUS_RECOVER_DONE; |
386 | goto out_complete; | 408 | goto out_complete; |
387 | } else { | ||
388 | /* Master is not currently active, irq was for someone else. */ | ||
389 | if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE) | ||
390 | goto out_no_complete; | ||
391 | } | 409 | } |
392 | 410 | ||
393 | /* | 411 | /* |
@@ -399,11 +417,32 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) | |||
399 | if (ret) { | 417 | if (ret) { |
400 | dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", | 418 | dev_dbg(bus->dev, "received error interrupt: 0x%08x\n", |
401 | irq_status); | 419 | irq_status); |
402 | bus->cmd_err = ret; | ||
403 | bus->master_state = ASPEED_I2C_MASTER_INACTIVE; | ||
404 | irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); | 420 | irq_handled |= (irq_status & ASPEED_I2CD_INTR_MASTER_ERRORS); |
405 | goto out_complete; | 421 | if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { |
422 | bus->cmd_err = ret; | ||
423 | bus->master_state = ASPEED_I2C_MASTER_INACTIVE; | ||
424 | goto out_complete; | ||
425 | } | ||
426 | } | ||
427 | |||
428 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | ||
429 | /* | ||
430 | * A pending master command will be started by H/W when the bus comes | ||
431 | * back to idle state after completing a slave operation so change the | ||
432 | * master state from 'pending' to 'start' at here if slave is inactive. | ||
433 | */ | ||
434 | if (bus->master_state == ASPEED_I2C_MASTER_PENDING) { | ||
435 | if (bus->slave_state != ASPEED_I2C_SLAVE_INACTIVE) | ||
436 | goto out_no_complete; | ||
437 | |||
438 | bus->master_state = ASPEED_I2C_MASTER_START; | ||
406 | } | 439 | } |
440 | #endif /* CONFIG_I2C_SLAVE */ | ||
441 | |||
442 | /* Master is not currently active, irq was for someone else. */ | ||
443 | if (bus->master_state == ASPEED_I2C_MASTER_INACTIVE || | ||
444 | bus->master_state == ASPEED_I2C_MASTER_PENDING) | ||
445 | goto out_no_complete; | ||
407 | 446 | ||
408 | /* We are in an invalid state; reset bus to a known state. */ | 447 | /* We are in an invalid state; reset bus to a known state. */ |
409 | if (!bus->msgs) { | 448 | if (!bus->msgs) { |
@@ -423,6 +462,20 @@ static u32 aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus, u32 irq_status) | |||
423 | * then update the state and handle the new state below. | 462 | * then update the state and handle the new state below. |
424 | */ | 463 | */ |
425 | if (bus->master_state == ASPEED_I2C_MASTER_START) { | 464 | if (bus->master_state == ASPEED_I2C_MASTER_START) { |
465 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | ||
466 | /* | ||
467 | * If a peer master starts a xfer immediately after it queues a | ||
468 | * master command, change its state to 'pending' then H/W will | ||
469 | * continue the queued master xfer just after completing the | ||
470 | * slave mode session. | ||
471 | */ | ||
472 | if (unlikely(irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH)) { | ||
473 | bus->master_state = ASPEED_I2C_MASTER_PENDING; | ||
474 | dev_dbg(bus->dev, | ||
475 | "master goes pending due to a slave start\n"); | ||
476 | goto out_no_complete; | ||
477 | } | ||
478 | #endif /* CONFIG_I2C_SLAVE */ | ||
426 | if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { | 479 | if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_ACK))) { |
427 | if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { | 480 | if (unlikely(!(irq_status & ASPEED_I2CD_INTR_TX_NAK))) { |
428 | bus->cmd_err = -ENXIO; | 481 | bus->cmd_err = -ENXIO; |
@@ -566,7 +619,8 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) | |||
566 | * interrupt bits. Each case needs to be handled using corresponding | 619 | * interrupt bits. Each case needs to be handled using corresponding |
567 | * handlers depending on the current state. | 620 | * handlers depending on the current state. |
568 | */ | 621 | */ |
569 | if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE) { | 622 | if (bus->master_state != ASPEED_I2C_MASTER_INACTIVE && |
623 | bus->master_state != ASPEED_I2C_MASTER_PENDING) { | ||
570 | irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); | 624 | irq_handled = aspeed_i2c_master_irq(bus, irq_remaining); |
571 | irq_remaining &= ~irq_handled; | 625 | irq_remaining &= ~irq_handled; |
572 | if (irq_remaining) | 626 | if (irq_remaining) |
@@ -601,15 +655,16 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, | |||
601 | { | 655 | { |
602 | struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); | 656 | struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap); |
603 | unsigned long time_left, flags; | 657 | unsigned long time_left, flags; |
604 | int ret = 0; | ||
605 | 658 | ||
606 | spin_lock_irqsave(&bus->lock, flags); | 659 | spin_lock_irqsave(&bus->lock, flags); |
607 | bus->cmd_err = 0; | 660 | bus->cmd_err = 0; |
608 | 661 | ||
609 | /* If bus is busy, attempt recovery. We assume a single master | 662 | /* If bus is busy in a single master environment, attempt recovery. */ |
610 | * environment. | 663 | if (!bus->multi_master && |
611 | */ | 664 | (readl(bus->base + ASPEED_I2C_CMD_REG) & |
612 | if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) { | 665 | ASPEED_I2CD_BUS_BUSY_STS)) { |
666 | int ret; | ||
667 | |||
613 | spin_unlock_irqrestore(&bus->lock, flags); | 668 | spin_unlock_irqrestore(&bus->lock, flags); |
614 | ret = aspeed_i2c_recover_bus(bus); | 669 | ret = aspeed_i2c_recover_bus(bus); |
615 | if (ret) | 670 | if (ret) |
@@ -629,10 +684,20 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, | |||
629 | time_left = wait_for_completion_timeout(&bus->cmd_complete, | 684 | time_left = wait_for_completion_timeout(&bus->cmd_complete, |
630 | bus->adap.timeout); | 685 | bus->adap.timeout); |
631 | 686 | ||
632 | if (time_left == 0) | 687 | if (time_left == 0) { |
688 | /* | ||
689 | * If timed out and bus is still busy in a multi master | ||
690 | * environment, attempt recovery at here. | ||
691 | */ | ||
692 | if (bus->multi_master && | ||
693 | (readl(bus->base + ASPEED_I2C_CMD_REG) & | ||
694 | ASPEED_I2CD_BUS_BUSY_STS)) | ||
695 | aspeed_i2c_recover_bus(bus); | ||
696 | |||
633 | return -ETIMEDOUT; | 697 | return -ETIMEDOUT; |
634 | else | 698 | } |
635 | return bus->master_xfer_result; | 699 | |
700 | return bus->master_xfer_result; | ||
636 | } | 701 | } |
637 | 702 | ||
638 | static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) | 703 | static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) |
@@ -672,7 +737,7 @@ static int aspeed_i2c_reg_slave(struct i2c_client *client) | |||
672 | __aspeed_i2c_reg_slave(bus, client->addr); | 737 | __aspeed_i2c_reg_slave(bus, client->addr); |
673 | 738 | ||
674 | bus->slave = client; | 739 | bus->slave = client; |
675 | bus->slave_state = ASPEED_I2C_SLAVE_STOP; | 740 | bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE; |
676 | spin_unlock_irqrestore(&bus->lock, flags); | 741 | spin_unlock_irqrestore(&bus->lock, flags); |
677 | 742 | ||
678 | return 0; | 743 | return 0; |
@@ -827,7 +892,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, | |||
827 | if (ret < 0) | 892 | if (ret < 0) |
828 | return ret; | 893 | return ret; |
829 | 894 | ||
830 | if (!of_property_read_bool(pdev->dev.of_node, "multi-master")) | 895 | if (of_property_read_bool(pdev->dev.of_node, "multi-master")) |
896 | bus->multi_master = true; | ||
897 | else | ||
831 | fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; | 898 | fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS; |
832 | 899 | ||
833 | /* Enable Master Mode */ | 900 | /* Enable Master Mode */ |