diff options
author | Brendan Higgins <brendanhiggins@google.com> | 2017-06-20 17:15:16 -0400 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2017-06-23 14:39:38 -0400 |
commit | f9eb91350bb20b3e881bc59158071e14ee4f0326 (patch) | |
tree | a1a0cad732d7e7872db577e03516f554a152374e | |
parent | f327c686d3ba44eda79a2d9e02a6a242e0b75787 (diff) |
i2c: aspeed: added slave support for Aspeed I2C driver
Added slave support for Aspeed I2C controller. Supports fourteen busses
present in AST24XX and AST25XX BMC SoCs by Aspeed.
Signed-off-by: Brendan Higgins <brendanhiggins@google.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
-rw-r--r-- | drivers/i2c/busses/i2c-aspeed.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 55f241f2cfcc..f19348328a71 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c | |||
@@ -49,6 +49,7 @@ | |||
49 | #define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8) | 49 | #define ASPEED_I2CD_SDA_DRIVE_1T_EN BIT(8) |
50 | #define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7) | 50 | #define ASPEED_I2CD_M_SDA_DRIVE_1T_EN BIT(7) |
51 | #define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6) | 51 | #define ASPEED_I2CD_M_HIGH_SPEED_EN BIT(6) |
52 | #define ASPEED_I2CD_SLAVE_EN BIT(1) | ||
52 | #define ASPEED_I2CD_MASTER_EN BIT(0) | 53 | #define ASPEED_I2CD_MASTER_EN BIT(0) |
53 | 54 | ||
54 | /* 0x04 : I2CD Clock and AC Timing Control Register #1 */ | 55 | /* 0x04 : I2CD Clock and AC Timing Control Register #1 */ |
@@ -69,6 +70,7 @@ | |||
69 | */ | 70 | */ |
70 | #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14) | 71 | #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT BIT(14) |
71 | #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13) | 72 | #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE BIT(13) |
73 | #define ASPEED_I2CD_INTR_SLAVE_MATCH BIT(7) | ||
72 | #define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6) | 74 | #define ASPEED_I2CD_INTR_SCL_TIMEOUT BIT(6) |
73 | #define ASPEED_I2CD_INTR_ABNORMAL BIT(5) | 75 | #define ASPEED_I2CD_INTR_ABNORMAL BIT(5) |
74 | #define ASPEED_I2CD_INTR_NORMAL_STOP BIT(4) | 76 | #define ASPEED_I2CD_INTR_NORMAL_STOP BIT(4) |
@@ -101,6 +103,9 @@ | |||
101 | #define ASPEED_I2CD_M_TX_CMD BIT(1) | 103 | #define ASPEED_I2CD_M_TX_CMD BIT(1) |
102 | #define ASPEED_I2CD_M_START_CMD BIT(0) | 104 | #define ASPEED_I2CD_M_START_CMD BIT(0) |
103 | 105 | ||
106 | /* 0x18 : I2CD Slave Device Address Register */ | ||
107 | #define ASPEED_I2CD_DEV_ADDR_MASK GENMASK(6, 0) | ||
108 | |||
104 | enum aspeed_i2c_master_state { | 109 | enum aspeed_i2c_master_state { |
105 | ASPEED_I2C_MASTER_START, | 110 | ASPEED_I2C_MASTER_START, |
106 | ASPEED_I2C_MASTER_TX_FIRST, | 111 | ASPEED_I2C_MASTER_TX_FIRST, |
@@ -111,6 +116,15 @@ enum aspeed_i2c_master_state { | |||
111 | ASPEED_I2C_MASTER_INACTIVE, | 116 | ASPEED_I2C_MASTER_INACTIVE, |
112 | }; | 117 | }; |
113 | 118 | ||
119 | enum aspeed_i2c_slave_state { | ||
120 | ASPEED_I2C_SLAVE_START, | ||
121 | ASPEED_I2C_SLAVE_READ_REQUESTED, | ||
122 | ASPEED_I2C_SLAVE_READ_PROCESSED, | ||
123 | ASPEED_I2C_SLAVE_WRITE_REQUESTED, | ||
124 | ASPEED_I2C_SLAVE_WRITE_RECEIVED, | ||
125 | ASPEED_I2C_SLAVE_STOP, | ||
126 | }; | ||
127 | |||
114 | struct aspeed_i2c_bus { | 128 | struct aspeed_i2c_bus { |
115 | struct i2c_adapter adap; | 129 | struct i2c_adapter adap; |
116 | struct device *dev; | 130 | struct device *dev; |
@@ -130,6 +144,10 @@ struct aspeed_i2c_bus { | |||
130 | int cmd_err; | 144 | int cmd_err; |
131 | /* Protected only by i2c_lock_bus */ | 145 | /* Protected only by i2c_lock_bus */ |
132 | int master_xfer_result; | 146 | int master_xfer_result; |
147 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | ||
148 | struct i2c_client *slave; | ||
149 | enum aspeed_i2c_slave_state slave_state; | ||
150 | #endif /* CONFIG_I2C_SLAVE */ | ||
133 | }; | 151 | }; |
134 | 152 | ||
135 | static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus); | 153 | static int aspeed_i2c_reset(struct aspeed_i2c_bus *bus); |
@@ -202,6 +220,110 @@ reset_out: | |||
202 | return aspeed_i2c_reset(bus); | 220 | return aspeed_i2c_reset(bus); |
203 | } | 221 | } |
204 | 222 | ||
223 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | ||
224 | static bool aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus) | ||
225 | { | ||
226 | u32 command, irq_status, status_ack = 0; | ||
227 | struct i2c_client *slave = bus->slave; | ||
228 | bool irq_handled = true; | ||
229 | u8 value; | ||
230 | |||
231 | spin_lock(&bus->lock); | ||
232 | if (!slave) { | ||
233 | irq_handled = false; | ||
234 | goto out; | ||
235 | } | ||
236 | |||
237 | command = readl(bus->base + ASPEED_I2C_CMD_REG); | ||
238 | irq_status = readl(bus->base + ASPEED_I2C_INTR_STS_REG); | ||
239 | |||
240 | /* Slave was requested, restart state machine. */ | ||
241 | if (irq_status & ASPEED_I2CD_INTR_SLAVE_MATCH) { | ||
242 | status_ack |= ASPEED_I2CD_INTR_SLAVE_MATCH; | ||
243 | bus->slave_state = ASPEED_I2C_SLAVE_START; | ||
244 | } | ||
245 | |||
246 | /* Slave is not currently active, irq was for someone else. */ | ||
247 | if (bus->slave_state == ASPEED_I2C_SLAVE_STOP) { | ||
248 | irq_handled = false; | ||
249 | goto out; | ||
250 | } | ||
251 | |||
252 | dev_dbg(bus->dev, "slave irq status 0x%08x, cmd 0x%08x\n", | ||
253 | irq_status, command); | ||
254 | |||
255 | /* Slave was sent something. */ | ||
256 | if (irq_status & ASPEED_I2CD_INTR_RX_DONE) { | ||
257 | value = readl(bus->base + ASPEED_I2C_BYTE_BUF_REG) >> 8; | ||
258 | /* Handle address frame. */ | ||
259 | if (bus->slave_state == ASPEED_I2C_SLAVE_START) { | ||
260 | if (value & 0x1) | ||
261 | bus->slave_state = | ||
262 | ASPEED_I2C_SLAVE_READ_REQUESTED; | ||
263 | else | ||
264 | bus->slave_state = | ||
265 | ASPEED_I2C_SLAVE_WRITE_REQUESTED; | ||
266 | } | ||
267 | status_ack |= ASPEED_I2CD_INTR_RX_DONE; | ||
268 | } | ||
269 | |||
270 | /* Slave was asked to stop. */ | ||
271 | if (irq_status & ASPEED_I2CD_INTR_NORMAL_STOP) { | ||
272 | status_ack |= ASPEED_I2CD_INTR_NORMAL_STOP; | ||
273 | bus->slave_state = ASPEED_I2C_SLAVE_STOP; | ||
274 | } | ||
275 | if (irq_status & ASPEED_I2CD_INTR_TX_NAK) { | ||
276 | status_ack |= ASPEED_I2CD_INTR_TX_NAK; | ||
277 | bus->slave_state = ASPEED_I2C_SLAVE_STOP; | ||
278 | } | ||
279 | |||
280 | switch (bus->slave_state) { | ||
281 | case ASPEED_I2C_SLAVE_READ_REQUESTED: | ||
282 | if (irq_status & ASPEED_I2CD_INTR_TX_ACK) | ||
283 | dev_err(bus->dev, "Unexpected ACK on read request.\n"); | ||
284 | bus->slave_state = ASPEED_I2C_SLAVE_READ_PROCESSED; | ||
285 | |||
286 | i2c_slave_event(slave, I2C_SLAVE_READ_REQUESTED, &value); | ||
287 | writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); | ||
288 | writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); | ||
289 | break; | ||
290 | case ASPEED_I2C_SLAVE_READ_PROCESSED: | ||
291 | status_ack |= ASPEED_I2CD_INTR_TX_ACK; | ||
292 | if (!(irq_status & ASPEED_I2CD_INTR_TX_ACK)) | ||
293 | dev_err(bus->dev, | ||
294 | "Expected ACK after processed read.\n"); | ||
295 | i2c_slave_event(slave, I2C_SLAVE_READ_PROCESSED, &value); | ||
296 | writel(value, bus->base + ASPEED_I2C_BYTE_BUF_REG); | ||
297 | writel(ASPEED_I2CD_S_TX_CMD, bus->base + ASPEED_I2C_CMD_REG); | ||
298 | break; | ||
299 | case ASPEED_I2C_SLAVE_WRITE_REQUESTED: | ||
300 | bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED; | ||
301 | i2c_slave_event(slave, I2C_SLAVE_WRITE_REQUESTED, &value); | ||
302 | break; | ||
303 | case ASPEED_I2C_SLAVE_WRITE_RECEIVED: | ||
304 | i2c_slave_event(slave, I2C_SLAVE_WRITE_RECEIVED, &value); | ||
305 | break; | ||
306 | case ASPEED_I2C_SLAVE_STOP: | ||
307 | i2c_slave_event(slave, I2C_SLAVE_STOP, &value); | ||
308 | break; | ||
309 | default: | ||
310 | dev_err(bus->dev, "unhandled slave_state: %d\n", | ||
311 | bus->slave_state); | ||
312 | break; | ||
313 | } | ||
314 | |||
315 | if (status_ack != irq_status) | ||
316 | dev_err(bus->dev, | ||
317 | "irq handled != irq. expected %x, but was %x\n", | ||
318 | irq_status, status_ack); | ||
319 | writel(status_ack, bus->base + ASPEED_I2C_INTR_STS_REG); | ||
320 | |||
321 | out: | ||
322 | spin_unlock(&bus->lock); | ||
323 | return irq_handled; | ||
324 | } | ||
325 | #endif /* CONFIG_I2C_SLAVE */ | ||
326 | |||
205 | /* precondition: bus.lock has been acquired. */ | 327 | /* precondition: bus.lock has been acquired. */ |
206 | static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) | 328 | static void aspeed_i2c_do_start(struct aspeed_i2c_bus *bus) |
207 | { | 329 | { |
@@ -427,6 +549,13 @@ static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id) | |||
427 | { | 549 | { |
428 | struct aspeed_i2c_bus *bus = dev_id; | 550 | struct aspeed_i2c_bus *bus = dev_id; |
429 | 551 | ||
552 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | ||
553 | if (aspeed_i2c_slave_irq(bus)) { | ||
554 | dev_dbg(bus->dev, "irq handled by slave.\n"); | ||
555 | return IRQ_HANDLED; | ||
556 | } | ||
557 | #endif /* CONFIG_I2C_SLAVE */ | ||
558 | |||
430 | return aspeed_i2c_master_irq(bus) ? IRQ_HANDLED : IRQ_NONE; | 559 | return aspeed_i2c_master_irq(bus) ? IRQ_HANDLED : IRQ_NONE; |
431 | } | 560 | } |
432 | 561 | ||
@@ -474,9 +603,75 @@ static u32 aspeed_i2c_functionality(struct i2c_adapter *adap) | |||
474 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; | 603 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_SMBUS_BLOCK_DATA; |
475 | } | 604 | } |
476 | 605 | ||
606 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | ||
607 | /* precondition: bus.lock has been acquired. */ | ||
608 | static void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr) | ||
609 | { | ||
610 | u32 addr_reg_val, func_ctrl_reg_val; | ||
611 | |||
612 | /* Set slave addr. */ | ||
613 | addr_reg_val = readl(bus->base + ASPEED_I2C_DEV_ADDR_REG); | ||
614 | addr_reg_val &= ~ASPEED_I2CD_DEV_ADDR_MASK; | ||
615 | addr_reg_val |= slave_addr & ASPEED_I2CD_DEV_ADDR_MASK; | ||
616 | writel(addr_reg_val, bus->base + ASPEED_I2C_DEV_ADDR_REG); | ||
617 | |||
618 | /* Turn on slave mode. */ | ||
619 | func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG); | ||
620 | func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN; | ||
621 | writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG); | ||
622 | } | ||
623 | |||
624 | static int aspeed_i2c_reg_slave(struct i2c_client *client) | ||
625 | { | ||
626 | struct aspeed_i2c_bus *bus = i2c_get_adapdata(client->adapter); | ||
627 | unsigned long flags; | ||
628 | |||
629 | spin_lock_irqsave(&bus->lock, flags); | ||
630 | if (bus->slave) { | ||
631 | spin_unlock_irqrestore(&bus->lock, flags); | ||
632 | return -EINVAL; | ||
633 | } | ||
634 | |||
635 | __aspeed_i2c_reg_slave(bus, client->addr); | ||
636 | |||
637 | bus->slave = client; | ||
638 | bus->slave_state = ASPEED_I2C_SLAVE_STOP; | ||
639 | spin_unlock_irqrestore(&bus->lock, flags); | ||
640 | |||
641 | return 0; | ||
642 | } | ||
643 | |||
644 | static int aspeed_i2c_unreg_slave(struct i2c_client *client) | ||
645 | { | ||
646 | struct aspeed_i2c_bus *bus = i2c_get_adapdata(client->adapter); | ||
647 | u32 func_ctrl_reg_val; | ||
648 | unsigned long flags; | ||
649 | |||
650 | spin_lock_irqsave(&bus->lock, flags); | ||
651 | if (!bus->slave) { | ||
652 | spin_unlock_irqrestore(&bus->lock, flags); | ||
653 | return -EINVAL; | ||
654 | } | ||
655 | |||
656 | /* Turn off slave mode. */ | ||
657 | func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG); | ||
658 | func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN; | ||
659 | writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG); | ||
660 | |||
661 | bus->slave = NULL; | ||
662 | spin_unlock_irqrestore(&bus->lock, flags); | ||
663 | |||
664 | return 0; | ||
665 | } | ||
666 | #endif /* CONFIG_I2C_SLAVE */ | ||
667 | |||
477 | static const struct i2c_algorithm aspeed_i2c_algo = { | 668 | static const struct i2c_algorithm aspeed_i2c_algo = { |
478 | .master_xfer = aspeed_i2c_master_xfer, | 669 | .master_xfer = aspeed_i2c_master_xfer, |
479 | .functionality = aspeed_i2c_functionality, | 670 | .functionality = aspeed_i2c_functionality, |
671 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | ||
672 | .reg_slave = aspeed_i2c_reg_slave, | ||
673 | .unreg_slave = aspeed_i2c_unreg_slave, | ||
674 | #endif /* CONFIG_I2C_SLAVE */ | ||
480 | }; | 675 | }; |
481 | 676 | ||
482 | static u32 aspeed_i2c_get_clk_reg_val(u32 divisor) | 677 | static u32 aspeed_i2c_get_clk_reg_val(u32 divisor) |
@@ -551,6 +746,12 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus, | |||
551 | writel(readl(bus->base + ASPEED_I2C_FUN_CTRL_REG) | fun_ctrl_reg, | 746 | writel(readl(bus->base + ASPEED_I2C_FUN_CTRL_REG) | fun_ctrl_reg, |
552 | bus->base + ASPEED_I2C_FUN_CTRL_REG); | 747 | bus->base + ASPEED_I2C_FUN_CTRL_REG); |
553 | 748 | ||
749 | #if IS_ENABLED(CONFIG_I2C_SLAVE) | ||
750 | /* If slave has already been registered, re-enable it. */ | ||
751 | if (bus->slave) | ||
752 | __aspeed_i2c_reg_slave(bus, bus->slave->addr); | ||
753 | #endif /* CONFIG_I2C_SLAVE */ | ||
754 | |||
554 | /* Set interrupt generation of I2C controller */ | 755 | /* Set interrupt generation of I2C controller */ |
555 | writel(ASPEED_I2CD_INTR_ALL, bus->base + ASPEED_I2C_INTR_CTRL_REG); | 756 | writel(ASPEED_I2CD_INTR_ALL, bus->base + ASPEED_I2C_INTR_CTRL_REG); |
556 | 757 | ||