1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // Copyright IBM Corp 2019 |
3 | |
4 | #include <linux/device.h> |
5 | #include <linux/errno.h> |
6 | #include <linux/fsi-occ.h> |
7 | #include <linux/i2c.h> |
8 | #include <linux/jiffies.h> |
9 | #include <linux/module.h> |
10 | #include <linux/sched.h> |
11 | #include <asm/unaligned.h> |
12 | |
13 | #include "common.h" |
14 | |
15 | #define OCC_TIMEOUT_MS 1000 |
16 | #define OCC_CMD_IN_PRG_WAIT_MS 50 |
17 | |
18 | /* OCB (on-chip control bridge - interface to OCC) registers */ |
19 | #define OCB_DATA1 0x6B035 |
20 | #define OCB_ADDR 0x6B070 |
21 | #define OCB_DATA3 0x6B075 |
22 | |
23 | /* OCC SRAM address space */ |
24 | #define OCC_SRAM_ADDR_CMD 0xFFFF6000 |
25 | #define OCC_SRAM_ADDR_RESP 0xFFFF7000 |
26 | |
27 | #define OCC_DATA_ATTN 0x20010000 |
28 | |
29 | struct p8_i2c_occ { |
30 | struct occ occ; |
31 | struct i2c_client *client; |
32 | }; |
33 | |
34 | #define to_p8_i2c_occ(x) container_of((x), struct p8_i2c_occ, occ) |
35 | |
36 | static int p8_i2c_occ_getscom(struct i2c_client *client, u32 address, u8 *data) |
37 | { |
38 | ssize_t rc; |
39 | __be64 buf; |
40 | struct i2c_msg msgs[2]; |
41 | |
42 | /* p8 i2c slave requires shift */ |
43 | address <<= 1; |
44 | |
45 | msgs[0].addr = client->addr; |
46 | msgs[0].flags = client->flags & I2C_M_TEN; |
47 | msgs[0].len = sizeof(u32); |
48 | /* address is a scom address; bus-endian */ |
49 | msgs[0].buf = (char *)&address; |
50 | |
51 | /* data from OCC is big-endian */ |
52 | msgs[1].addr = client->addr; |
53 | msgs[1].flags = (client->flags & I2C_M_TEN) | I2C_M_RD; |
54 | msgs[1].len = sizeof(u64); |
55 | msgs[1].buf = (char *)&buf; |
56 | |
57 | rc = i2c_transfer(adap: client->adapter, msgs, num: 2); |
58 | if (rc < 0) |
59 | return rc; |
60 | |
61 | *(u64 *)data = be64_to_cpu(buf); |
62 | |
63 | return 0; |
64 | } |
65 | |
66 | static int p8_i2c_occ_putscom(struct i2c_client *client, u32 address, u8 *data) |
67 | { |
68 | u32 buf[3]; |
69 | ssize_t rc; |
70 | |
71 | /* p8 i2c slave requires shift */ |
72 | address <<= 1; |
73 | |
74 | /* address is bus-endian; data passed through from user as-is */ |
75 | buf[0] = address; |
76 | memcpy(&buf[1], &data[4], sizeof(u32)); |
77 | memcpy(&buf[2], data, sizeof(u32)); |
78 | |
79 | rc = i2c_master_send(client, buf: (const char *)buf, count: sizeof(buf)); |
80 | if (rc < 0) |
81 | return rc; |
82 | else if (rc != sizeof(buf)) |
83 | return -EIO; |
84 | |
85 | return 0; |
86 | } |
87 | |
88 | static int p8_i2c_occ_putscom_u32(struct i2c_client *client, u32 address, |
89 | u32 data0, u32 data1) |
90 | { |
91 | u8 buf[8]; |
92 | |
93 | memcpy(buf, &data0, 4); |
94 | memcpy(buf + 4, &data1, 4); |
95 | |
96 | return p8_i2c_occ_putscom(client, address, data: buf); |
97 | } |
98 | |
99 | static int p8_i2c_occ_putscom_be(struct i2c_client *client, u32 address, |
100 | u8 *data, size_t len) |
101 | { |
102 | __be32 data0 = 0, data1 = 0; |
103 | |
104 | memcpy(&data0, data, min_t(size_t, len, 4)); |
105 | if (len > 4) { |
106 | len -= 4; |
107 | memcpy(&data1, data + 4, min_t(size_t, len, 4)); |
108 | } |
109 | |
110 | return p8_i2c_occ_putscom_u32(client, address, be32_to_cpu(data0), |
111 | be32_to_cpu(data1)); |
112 | } |
113 | |
114 | static int p8_i2c_occ_send_cmd(struct occ *occ, u8 *cmd, size_t len, |
115 | void *resp, size_t resp_len) |
116 | { |
117 | int i, rc; |
118 | unsigned long start; |
119 | u16 data_length; |
120 | const unsigned long timeout = msecs_to_jiffies(OCC_TIMEOUT_MS); |
121 | const long wait_time = msecs_to_jiffies(OCC_CMD_IN_PRG_WAIT_MS); |
122 | struct p8_i2c_occ *ctx = to_p8_i2c_occ(occ); |
123 | struct i2c_client *client = ctx->client; |
124 | struct occ_response *or = (struct occ_response *)resp; |
125 | |
126 | start = jiffies; |
127 | |
128 | /* set sram address for command */ |
129 | rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR, OCC_SRAM_ADDR_CMD, data1: 0); |
130 | if (rc) |
131 | return rc; |
132 | |
133 | /* write command (expected to already be BE), we need bus-endian... */ |
134 | rc = p8_i2c_occ_putscom_be(client, OCB_DATA3, data: cmd, len); |
135 | if (rc) |
136 | return rc; |
137 | |
138 | /* trigger OCC attention */ |
139 | rc = p8_i2c_occ_putscom_u32(client, OCB_DATA1, OCC_DATA_ATTN, data1: 0); |
140 | if (rc) |
141 | return rc; |
142 | |
143 | do { |
144 | /* set sram address for response */ |
145 | rc = p8_i2c_occ_putscom_u32(client, OCB_ADDR, |
146 | OCC_SRAM_ADDR_RESP, data1: 0); |
147 | if (rc) |
148 | return rc; |
149 | |
150 | rc = p8_i2c_occ_getscom(client, OCB_DATA3, data: (u8 *)resp); |
151 | if (rc) |
152 | return rc; |
153 | |
154 | /* wait for OCC */ |
155 | if (or->return_status == OCC_RESP_CMD_IN_PRG) { |
156 | rc = -EALREADY; |
157 | |
158 | if (time_after(jiffies, start + timeout)) |
159 | break; |
160 | |
161 | set_current_state(TASK_INTERRUPTIBLE); |
162 | schedule_timeout(timeout: wait_time); |
163 | } |
164 | } while (rc); |
165 | |
166 | /* check the OCC response */ |
167 | switch (or->return_status) { |
168 | case OCC_RESP_CMD_IN_PRG: |
169 | rc = -ETIMEDOUT; |
170 | break; |
171 | case OCC_RESP_SUCCESS: |
172 | rc = 0; |
173 | break; |
174 | case OCC_RESP_CMD_INVAL: |
175 | case OCC_RESP_CMD_LEN_INVAL: |
176 | case OCC_RESP_DATA_INVAL: |
177 | case OCC_RESP_CHKSUM_ERR: |
178 | rc = -EINVAL; |
179 | break; |
180 | case OCC_RESP_INT_ERR: |
181 | case OCC_RESP_BAD_STATE: |
182 | case OCC_RESP_CRIT_EXCEPT: |
183 | case OCC_RESP_CRIT_INIT: |
184 | case OCC_RESP_CRIT_WATCHDOG: |
185 | case OCC_RESP_CRIT_OCB: |
186 | case OCC_RESP_CRIT_HW: |
187 | rc = -EREMOTEIO; |
188 | break; |
189 | default: |
190 | rc = -EPROTO; |
191 | } |
192 | |
193 | if (rc < 0) |
194 | return rc; |
195 | |
196 | data_length = get_unaligned_be16(p: &or->data_length); |
197 | if ((data_length + 7) > resp_len) |
198 | return -EMSGSIZE; |
199 | |
200 | /* fetch the rest of the response data */ |
201 | for (i = 8; i < data_length + 7; i += 8) { |
202 | rc = p8_i2c_occ_getscom(client, OCB_DATA3, data: ((u8 *)resp) + i); |
203 | if (rc) |
204 | return rc; |
205 | } |
206 | |
207 | return 0; |
208 | } |
209 | |
210 | static int p8_i2c_occ_probe(struct i2c_client *client) |
211 | { |
212 | struct occ *occ; |
213 | struct p8_i2c_occ *ctx = devm_kzalloc(dev: &client->dev, size: sizeof(*ctx), |
214 | GFP_KERNEL); |
215 | if (!ctx) |
216 | return -ENOMEM; |
217 | |
218 | ctx->client = client; |
219 | occ = &ctx->occ; |
220 | occ->bus_dev = &client->dev; |
221 | dev_set_drvdata(dev: &client->dev, data: occ); |
222 | |
223 | occ->powr_sample_time_us = 250; |
224 | occ->poll_cmd_data = 0x10; /* P8 OCC poll data */ |
225 | occ->send_cmd = p8_i2c_occ_send_cmd; |
226 | |
227 | return occ_setup(occ); |
228 | } |
229 | |
230 | static void p8_i2c_occ_remove(struct i2c_client *client) |
231 | { |
232 | struct occ *occ = dev_get_drvdata(dev: &client->dev); |
233 | |
234 | occ_shutdown(occ); |
235 | } |
236 | |
237 | static const struct of_device_id p8_i2c_occ_of_match[] = { |
238 | { .compatible = "ibm,p8-occ-hwmon" }, |
239 | {} |
240 | }; |
241 | MODULE_DEVICE_TABLE(of, p8_i2c_occ_of_match); |
242 | |
243 | static struct i2c_driver p8_i2c_occ_driver = { |
244 | .class = I2C_CLASS_HWMON, |
245 | .driver = { |
246 | .name = "occ-hwmon" , |
247 | .of_match_table = p8_i2c_occ_of_match, |
248 | }, |
249 | .probe = p8_i2c_occ_probe, |
250 | .remove = p8_i2c_occ_remove, |
251 | }; |
252 | |
253 | module_i2c_driver(p8_i2c_occ_driver); |
254 | |
255 | MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>" ); |
256 | MODULE_DESCRIPTION("BMC P8 OCC hwmon driver" ); |
257 | MODULE_LICENSE("GPL" ); |
258 | |