Skip to content

Commit 88be37c

Browse files
Eddie Jamesgroeck
authored andcommitted
hwmon (occ): Add command transport method for P8 and P9
For the P8 OCC, add the procedure to send a command to the OCC over I2C bus. This involves writing the OCC command registers with serial communication operations (SCOMs) interpreted by the I2C slave. For the P9 OCC, add a procedure to use the OCC in-kernel API to send a command to the OCC through the SBE. Signed-off-by: Eddie James <eajames@linux.ibm.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
1 parent 5b5513b commit 88be37c

File tree

2 files changed

+221
-2
lines changed

2 files changed

+221
-2
lines changed

drivers/hwmon/occ/p8_i2c.c

Lines changed: 184 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,204 @@
22

33
#include <linux/device.h>
44
#include <linux/errno.h>
5+
#include <linux/fsi-occ.h>
56
#include <linux/i2c.h>
7+
#include <linux/jiffies.h>
68
#include <linux/module.h>
9+
#include <linux/sched.h>
10+
#include <asm/unaligned.h>
711

812
#include "common.h"
913

14+
#define OCC_TIMEOUT_MS 1000
15+
#define OCC_CMD_IN_PRG_WAIT_MS 50
16+
17+
/* OCB (on-chip control bridge - interface to OCC) registers */
18+
#define OCB_DATA1 0x6B035
19+
#define OCB_ADDR 0x6B070
20+
#define OCB_DATA3 0x6B075
21+
22+
/* OCC SRAM address space */
23+
#define OCC_SRAM_ADDR_CMD 0xFFFF6000
24+
#define OCC_SRAM_ADDR_RESP 0xFFFF7000
25+
26+
#define OCC_DATA_ATTN 0x20010000
27+
1028
struct p8_i2c_occ {
1129
struct occ occ;
1230
struct i2c_client *client;
1331
};
1432

1533
#define to_p8_i2c_occ(x) container_of((x), struct p8_i2c_occ, occ)
1634

35+
static int p8_i2c_occ_getscom(struct i2c_client *client, u32 address, u8 *data)
36+
{
37+
ssize_t rc;
38+
__be64 buf;
39+
struct i2c_msg msgs[2];
40+
41+
/* p8 i2c slave requires shift */
42+
address <<= 1;
43+
44+
msgs[0].addr = client->addr;
45+
msgs[0].flags = client->flags & I2C_M_TEN;
46+
msgs[0].len = sizeof(u32);
47+
/* address is a scom address; bus-endian */
48+
msgs[0].buf = (char *)&address;
49+
50+
/* data from OCC is big-endian */
51+
msgs[1].addr = client->addr;
52+
msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD;
53+
msgs[1].len = sizeof(u64);
54+
msgs[1].buf = (char *)&buf;
55+
56+
rc = i2c_transfer(client->adapter, msgs, 2);
57+
if (rc < 0)
58+
return rc;
59+
60+
*(u64 *)data = be64_to_cpu(buf);
61+
62+
return 0;
63+
}
64+
65+
static int p8_i2c_occ_putscom(struct i2c_client *client, u32 address, u8 *data)
66+
{
67+
u32 buf[3];
68+
ssize_t rc;
69+
70+
/* p8 i2c slave requires shift */
71+
address <<= 1;
72+
73+
/* address is bus-endian; data passed through from user as-is */
74+
buf[0] = address;
75+
memcpy(&buf[1], &data[4], sizeof(u32));
76+
memcpy(&buf[2], data, sizeof(u32));
77+
78+
rc = i2c_master_send(client, (const char *)buf, sizeof(buf));
79+
if (rc < 0)
80+
return rc;
81+
else if (rc != sizeof(buf))
82+
return -EIO;
83+
84+
return 0;
85+
}
86+
87+
static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address,
88+
u32 data0, u32 data1)
89+
{
90+
u8 buf[8];
91+
92+
memcpy(buf, &data0, 4);
93+
memcpy(buf + 4, &data1, 4);
94+
95+
return p8_i2c_occ_putscom(client, address, buf);
96+
}
97+
98+
static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address,
99+
u8 *data)
100+
{
101+
__be32 data0, data1;
102+
103+
memcpy(&data0, data, 4);
104+
memcpy(&data1, data + 4, 4);
105+
106+
return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0),
107+
be32_to_cpu(data1));
108+
}
109+
17110
static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd)
18111
{
19-
return -EOPNOTSUPP;
112+
int i, rc;
113+
unsigned long start;
114+
u16 data_length;
115+
const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS);
116+
const long wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS);
117+
struct p8_i2c_occ *ctx = to_p8_i2c_occ(occ);
118+
struct i2c_client *client = ctx->client;
119+
struct occ_response *resp = &occ->resp;
120+
121+
start = jiffies;
122+
123+
/* set sram address for command */
124+
rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR, OCC_SRAM_ADDR_CMD, 0);
125+
if (rc)
126+
return rc;
127+
128+
/* write command (expected to already be BE), we need bus-endian... */
129+
rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, cmd);
130+
if (rc)
131+
return rc;
132+
133+
/* trigger OCC attention */
134+
rc = p8_i2c_occ_putscom_u32(client, OCB_DATA1, OCC_DATA_ATTN, 0);
135+
if (rc)
136+
return rc;
137+
138+
do {
139+
/* set sram address for response */
140+
rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR,
141+
OCC_SRAM_ADDR_RESP, 0);
142+
if (rc)
143+
return rc;
144+
145+
rc = p8_i2c_occ_getscom(client, OCB_DATA3, (u8 *)resp);
146+
if (rc)
147+
return rc;
148+
149+
/* wait for OCC */
150+
if (resp->return_status == OCC_RESP_CMD_IN_PRG) {
151+
rc = -EALREADY;
152+
153+
if (time_after(jiffies, start + timeout))
154+
break;
155+
156+
set_current_state(TASK_INTERRUPTIBLE);
157+
schedule_timeout(wait_time);
158+
}
159+
} while (rc);
160+
161+
/* check the OCC response */
162+
switch (resp->return_status) {
163+
case OCC_RESP_CMD_IN_PRG:
164+
rc = -ETIMEDOUT;
165+
break;
166+
case OCC_RESP_SUCCESS:
167+
rc = 0;
168+
break;
169+
case OCC_RESP_CMD_INVAL:
170+
case OCC_RESP_CMD_LEN_INVAL:
171+
case OCC_RESP_DATA_INVAL:
172+
case OCC_RESP_CHKSUM_ERR:
173+
rc = -EINVAL;
174+
break;
175+
case OCC_RESP_INT_ERR:
176+
case OCC_RESP_BAD_STATE:
177+
case OCC_RESP_CRIT_EXCEPT:
178+
case OCC_RESP_CRIT_INIT:
179+
case OCC_RESP_CRIT_WATCHDOG:
180+
case OCC_RESP_CRIT_OCB:
181+
case OCC_RESP_CRIT_HW:
182+
rc = -EREMOTEIO;
183+
break;
184+
default:
185+
rc = -EPROTO;
186+
}
187+
188+
if (rc < 0)
189+
return rc;
190+
191+
data_length = get_unaligned_be16(&resp->data_length);
192+
if (data_length > OCC_RESP_DATA_BYTES)
193+
return -EMSGSIZE;
194+
195+
/* fetch the rest of the response data */
196+
for (i = 8; i < data_length + 7; i += 8) {
197+
rc = p8_i2c_occ_getscom(client, OCB_DATA3, ((u8 *)resp) + i);
198+
if (rc)
199+
return rc;
200+
}
201+
202+
return 0;
20203
}
21204

22205
static int p8_i2c_occ_probe(struct i2c_client *client,

drivers/hwmon/occ/p9_sbe.c

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <linux/device.h>
44
#include <linux/errno.h>
5+
#include <linux/fsi-occ.h>
56
#include <linux/module.h>
67
#include <linux/platform_device.h>
78

@@ -16,7 +17,42 @@ struct p9_sbe_occ {
1617

1718
static int p9_sbe_occ_send_cmd(struct occ *occ, u8 *cmd)
1819
{
19-
return -EOPNOTSUPP;
20+
struct occ_response *resp = &occ->resp;
21+
struct p9_sbe_occ *ctx = to_p9_sbe_occ(occ);
22+
size_t resp_len = sizeof(*resp);
23+
int rc;
24+
25+
rc = fsi_occ_submit(ctx->sbe, cmd, 8, resp, &resp_len);
26+
if (rc < 0)
27+
return rc;
28+
29+
switch (resp->return_status) {
30+
case OCC_RESP_CMD_IN_PRG:
31+
rc = -ETIMEDOUT;
32+
break;
33+
case OCC_RESP_SUCCESS:
34+
rc = 0;
35+
break;
36+
case OCC_RESP_CMD_INVAL:
37+
case OCC_RESP_CMD_LEN_INVAL:
38+
case OCC_RESP_DATA_INVAL:
39+
case OCC_RESP_CHKSUM_ERR:
40+
rc = -EINVAL;
41+
break;
42+
case OCC_RESP_INT_ERR:
43+
case OCC_RESP_BAD_STATE:
44+
case OCC_RESP_CRIT_EXCEPT:
45+
case OCC_RESP_CRIT_INIT:
46+
case OCC_RESP_CRIT_WATCHDOG:
47+
case OCC_RESP_CRIT_OCB:
48+
case OCC_RESP_CRIT_HW:
49+
rc = -EREMOTEIO;
50+
break;
51+
default:
52+
rc = -EPROTO;
53+
}
54+
55+
return rc;
2056
}
2157

2258
static int p9_sbe_occ_probe(struct platform_device *pdev)

0 commit comments

Comments
 (0)