summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0063-i2c-aspeed-add-general-call-support.patch
blob: 2f51c3fc9f736c8bc87708b01eb12d5df152a42d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
From 3f73215941667176ba05f358f4ee08816299bd32 Mon Sep 17 00:00:00 2001
From: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
Date: Wed, 1 May 2019 13:27:34 -0700
Subject: [PATCH] i2c: aspeed: add general call support

This commit adds general call support into Aspeed I2C driver.
This is downstream only customization so it should not go into
upstream.

Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@intel.com>
---
 .../devicetree/bindings/i2c/i2c-aspeed.txt         |  1 +
 drivers/i2c/busses/i2c-aspeed.c                    | 39 ++++++++++++++++++++++
 drivers/i2c/i2c-slave-mqueue.c                     |  4 ++-
 include/linux/i2c.h                                |  1 +
 4 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
index d3f4a39f7ba6..c1ee99398517 100644
--- a/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
+++ b/Documentation/devicetree/bindings/i2c/i2c-aspeed.txt
@@ -19,6 +19,7 @@ Optional Properties:
 - bus-frequency	: frequency of the bus clock in Hz defaults to 100 kHz when not
 		  specified
 - multi-master	: states that there is another master active on this bus.
+- general-call	: enables general call receiving.
 - bus-timeout-ms: bus timeout in milliseconds defaults to 1 second when not
 		  specified.
 - #retries	: Number of retries for master transfer.
diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 8dc6723bfaaf..891b2b5c4b7a 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -59,6 +59,7 @@
 #define ASPEED_I2CD_SDA_DRIVE_1T_EN			BIT(8)
 #define ASPEED_I2CD_M_SDA_DRIVE_1T_EN			BIT(7)
 #define ASPEED_I2CD_M_HIGH_SPEED_EN			BIT(6)
+#define ASPEED_I2CD_GCALL_EN				BIT(2)
 #define ASPEED_I2CD_SLAVE_EN				BIT(1)
 #define ASPEED_I2CD_MASTER_EN				BIT(0)
 
@@ -83,6 +84,7 @@
  */
 #define ASPEED_I2CD_INTR_SDA_DL_TIMEOUT			BIT(14)
 #define ASPEED_I2CD_INTR_BUS_RECOVER_DONE		BIT(13)
+#define ASPEED_I2CD_INTR_GCALL_ADDR			BIT(8)
 #define ASPEED_I2CD_INTR_SLAVE_MATCH			BIT(7)
 #define ASPEED_I2CD_INTR_SCL_TIMEOUT			BIT(6)
 #define ASPEED_I2CD_INTR_ABNORMAL			BIT(5)
@@ -167,6 +169,8 @@ enum aspeed_i2c_slave_state {
 	ASPEED_I2C_SLAVE_READ_PROCESSED,
 	ASPEED_I2C_SLAVE_WRITE_REQUESTED,
 	ASPEED_I2C_SLAVE_WRITE_RECEIVED,
+	ASPEED_I2C_SLAVE_GCALL_START,
+	ASPEED_I2C_SLAVE_GCALL_REQUESTED,
 	ASPEED_I2C_SLAVE_STOP,
 };
 
@@ -208,6 +212,8 @@ struct aspeed_i2c_bus {
 #if IS_ENABLED(CONFIG_I2C_SLAVE)
 	struct i2c_client		*slave;
 	enum aspeed_i2c_slave_state	slave_state;
+	/* General call */
+	bool				general_call;
 #endif /* CONFIG_I2C_SLAVE */
 };
 
@@ -315,6 +321,12 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 		bus->slave_state = ASPEED_I2C_SLAVE_START;
 	}
 
+	/* General call was requested, restart state machine. */
+	if (irq_status & ASPEED_I2CD_INTR_GCALL_ADDR) {
+		irq_handled |= ASPEED_I2CD_INTR_GCALL_ADDR;
+		bus->slave_state = ASPEED_I2C_SLAVE_GCALL_START;
+	}
+
 	/* Slave is not currently active, irq was for someone else. */
 	if (bus->slave_state == ASPEED_I2C_SLAVE_INACTIVE)
 		return irq_handled;
@@ -342,6 +354,21 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 			else
 				bus->slave_state =
 						ASPEED_I2C_SLAVE_WRITE_REQUESTED;
+		} else if (bus->slave_state == ASPEED_I2C_SLAVE_GCALL_START) {
+			/*
+			 * I2C spec defines the second byte meaning like below.
+			 * 0x06 : Reset and write programmable part of slave
+			 *        address by hardware.
+			 * 0x04 : Write programmable part of slave address by
+			 *        hardware.
+			 * 0x00 : No allowed.
+			 *
+			 * But in OpenBMC, we are going to use this
+			 * 'General call' feature for IPMB message broadcasting
+			 * so it delivers all data as is without any specific
+			 * handling of the second byte.
+			 */
+			bus->slave_state = ASPEED_I2C_SLAVE_GCALL_REQUESTED;
 		}
 		irq_handled |= ASPEED_I2CD_INTR_RX_DONE;
 	}
@@ -462,11 +489,16 @@ static u32 aspeed_i2c_slave_irq(struct aspeed_i2c_bus *bus, u32 irq_status)
 			       bus->base + ASPEED_I2C_CMD_REG);
 		}
 		break;
+	case ASPEED_I2C_SLAVE_GCALL_REQUESTED:
+		bus->slave_state = ASPEED_I2C_SLAVE_WRITE_RECEIVED;
+		i2c_slave_event(slave, I2C_SLAVE_GCALL_REQUESTED, &value);
+		break;
 	case ASPEED_I2C_SLAVE_STOP:
 		i2c_slave_event(slave, I2C_SLAVE_STOP, &value);
 		bus->slave_state = ASPEED_I2C_SLAVE_INACTIVE;
 		break;
 	case ASPEED_I2C_SLAVE_START:
+	case ASPEED_I2C_SLAVE_GCALL_START:
 		/* Slave was just started. Waiting for the next event. */;
 		break;
 	default:
@@ -1084,6 +1116,8 @@ static void __aspeed_i2c_reg_slave(struct aspeed_i2c_bus *bus, u16 slave_addr)
 	/* Turn on slave mode. */
 	func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
 	func_ctrl_reg_val |= ASPEED_I2CD_SLAVE_EN;
+	if (bus->general_call)
+		func_ctrl_reg_val |= ASPEED_I2CD_GCALL_EN;
 	writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
 }
 
@@ -1122,6 +1156,8 @@ static int aspeed_i2c_unreg_slave(struct i2c_client *client)
 	/* Turn off slave mode. */
 	func_ctrl_reg_val = readl(bus->base + ASPEED_I2C_FUN_CTRL_REG);
 	func_ctrl_reg_val &= ~ASPEED_I2CD_SLAVE_EN;
+	if (bus->general_call)
+		func_ctrl_reg_val &= ~ASPEED_I2CD_GCALL_EN;
 	writel(func_ctrl_reg_val, bus->base + ASPEED_I2C_FUN_CTRL_REG);
 
 	bus->slave = NULL;
@@ -1269,6 +1305,9 @@ static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
 	       bus->base + ASPEED_I2C_FUN_CTRL_REG);
 
 #if IS_ENABLED(CONFIG_I2C_SLAVE)
+	if (of_property_read_bool(pdev->dev.of_node, "general-call"))
+		bus->general_call = true;
+
 	/* If slave has already been registered, re-enable it. */
 	if (bus->slave)
 		__aspeed_i2c_reg_slave(bus, bus->slave->addr);
diff --git a/drivers/i2c/i2c-slave-mqueue.c b/drivers/i2c/i2c-slave-mqueue.c
index 2c7a6038409c..1d4db584b393 100644
--- a/drivers/i2c/i2c-slave-mqueue.c
+++ b/drivers/i2c/i2c-slave-mqueue.c
@@ -56,10 +56,12 @@ static int i2c_slave_mqueue_callback(struct i2c_client *client,
 
 	switch (event) {
 	case I2C_SLAVE_WRITE_REQUESTED:
+	case I2C_SLAVE_GCALL_REQUESTED:
 		mq->truncated = 0;
 
 		msg->len = 1;
-		msg->buf[0] = client->addr << 1;
+		msg->buf[0] = event == I2C_SLAVE_GCALL_REQUESTED ?
+			      0 : client->addr << 1;
 		break;
 
 	case I2C_SLAVE_WRITE_RECEIVED:
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index b4055d133338..52369ea150b4 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -365,6 +365,7 @@ enum i2c_slave_event {
 	I2C_SLAVE_WRITE_REQUESTED,
 	I2C_SLAVE_READ_PROCESSED,
 	I2C_SLAVE_WRITE_RECEIVED,
+	I2C_SLAVE_GCALL_REQUESTED,
 	I2C_SLAVE_STOP,
 };
 
-- 
2.7.4