Commit | Line | Data |
---|---|---|
886bdf9c HL |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * The Huawei Cache Coherence System (HCCS) is a multi-chip interconnection | |
4 | * bus protocol. | |
5 | * | |
6 | * Copyright (c) 2023 Hisilicon Limited. | |
7 | * Author: Huisong Li <lihuisong@huawei.com> | |
47f7a255 HL |
8 | * |
9 | * HCCS driver for Kunpeng SoC provides the following features: | |
10 | * - Retrieve the following information about each port: | |
11 | * - port type | |
12 | * - lane mode | |
13 | * - enable | |
14 | * - current lane mode | |
15 | * - link finite state machine | |
16 | * - lane mask | |
17 | * - CRC error count | |
18 | * | |
19 | * - Retrieve the following information about all the ports on the chip or | |
20 | * the die: | |
21 | * - if all enabled ports are in linked | |
22 | * - if all linked ports are in full lane | |
23 | * - CRC error count sum | |
886bdf9c HL |
24 | */ |
25 | #include <linux/acpi.h> | |
26 | #include <linux/iopoll.h> | |
27 | #include <linux/platform_device.h> | |
47f7a255 | 28 | #include <linux/sysfs.h> |
886bdf9c HL |
29 | |
30 | #include <acpi/pcc.h> | |
31 | ||
32 | #include "kunpeng_hccs.h" | |
33 | ||
886bdf9c HL |
34 | /* |
35 | * Arbitrary retries in case the remote processor is slow to respond | |
36 | * to PCC commands | |
37 | */ | |
38 | #define HCCS_PCC_CMD_WAIT_RETRIES_NUM 500ULL | |
39 | #define HCCS_POLL_STATUS_TIME_INTERVAL_US 3 | |
40 | ||
47f7a255 HL |
41 | static struct hccs_port_info *kobj_to_port_info(struct kobject *k) |
42 | { | |
43 | return container_of(k, struct hccs_port_info, kobj); | |
44 | } | |
45 | ||
46 | static struct hccs_die_info *kobj_to_die_info(struct kobject *k) | |
47 | { | |
48 | return container_of(k, struct hccs_die_info, kobj); | |
49 | } | |
50 | ||
51 | static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k) | |
52 | { | |
53 | return container_of(k, struct hccs_chip_info, kobj); | |
54 | } | |
55 | ||
886bdf9c HL |
56 | struct hccs_register_ctx { |
57 | struct device *dev; | |
58 | u8 chan_id; | |
59 | int err; | |
60 | }; | |
61 | ||
62 | static acpi_status hccs_get_register_cb(struct acpi_resource *ares, | |
63 | void *context) | |
64 | { | |
65 | struct acpi_resource_generic_register *reg; | |
66 | struct hccs_register_ctx *ctx = context; | |
67 | ||
68 | if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER) | |
69 | return AE_OK; | |
70 | ||
71 | reg = &ares->data.generic_reg; | |
72 | if (reg->space_id != ACPI_ADR_SPACE_PLATFORM_COMM) { | |
73 | dev_err(ctx->dev, "Bad register resource.\n"); | |
74 | ctx->err = -EINVAL; | |
75 | return AE_ERROR; | |
76 | } | |
77 | ctx->chan_id = reg->access_size; | |
78 | ||
79 | return AE_OK; | |
80 | } | |
81 | ||
82 | static int hccs_get_pcc_chan_id(struct hccs_dev *hdev) | |
83 | { | |
84 | acpi_handle handle = ACPI_HANDLE(hdev->dev); | |
85 | struct hccs_register_ctx ctx = {0}; | |
86 | acpi_status status; | |
87 | ||
e1e720f3 HL |
88 | if (!acpi_has_method(handle, METHOD_NAME__CRS)) { |
89 | dev_err(hdev->dev, "No _CRS method.\n"); | |
886bdf9c | 90 | return -ENODEV; |
e1e720f3 | 91 | } |
886bdf9c HL |
92 | |
93 | ctx.dev = hdev->dev; | |
94 | status = acpi_walk_resources(handle, METHOD_NAME__CRS, | |
95 | hccs_get_register_cb, &ctx); | |
96 | if (ACPI_FAILURE(status)) | |
97 | return ctx.err; | |
98 | hdev->chan_id = ctx.chan_id; | |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
103 | static void hccs_chan_tx_done(struct mbox_client *cl, void *msg, int ret) | |
104 | { | |
105 | if (ret < 0) | |
106 | pr_debug("TX did not complete: CMD sent:0x%x, ret:%d\n", | |
107 | *(u8 *)msg, ret); | |
108 | else | |
109 | pr_debug("TX completed. CMD sent:0x%x, ret:%d\n", | |
110 | *(u8 *)msg, ret); | |
111 | } | |
112 | ||
be2f78a8 HL |
113 | static void hccs_pcc_rx_callback(struct mbox_client *cl, void *mssg) |
114 | { | |
115 | struct hccs_mbox_client_info *cl_info = | |
116 | container_of(cl, struct hccs_mbox_client_info, client); | |
117 | ||
118 | complete(&cl_info->done); | |
119 | } | |
120 | ||
886bdf9c HL |
121 | static void hccs_unregister_pcc_channel(struct hccs_dev *hdev) |
122 | { | |
123 | struct hccs_mbox_client_info *cl_info = &hdev->cl_info; | |
124 | ||
125 | if (cl_info->pcc_comm_addr) | |
126 | iounmap(cl_info->pcc_comm_addr); | |
127 | pcc_mbox_free_channel(hdev->cl_info.pcc_chan); | |
128 | } | |
129 | ||
130 | static int hccs_register_pcc_channel(struct hccs_dev *hdev) | |
131 | { | |
132 | struct hccs_mbox_client_info *cl_info = &hdev->cl_info; | |
133 | struct mbox_client *cl = &cl_info->client; | |
134 | struct pcc_mbox_chan *pcc_chan; | |
135 | struct device *dev = hdev->dev; | |
136 | int rc; | |
137 | ||
138 | cl->dev = dev; | |
139 | cl->tx_block = false; | |
140 | cl->knows_txdone = true; | |
141 | cl->tx_done = hccs_chan_tx_done; | |
be2f78a8 HL |
142 | cl->rx_callback = hdev->verspec_data->rx_callback; |
143 | init_completion(&cl_info->done); | |
144 | ||
886bdf9c HL |
145 | pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id); |
146 | if (IS_ERR(pcc_chan)) { | |
147 | dev_err(dev, "PPC channel request failed.\n"); | |
148 | rc = -ENODEV; | |
149 | goto out; | |
150 | } | |
151 | cl_info->pcc_chan = pcc_chan; | |
152 | cl_info->mbox_chan = pcc_chan->mchan; | |
153 | ||
154 | /* | |
155 | * pcc_chan->latency is just a nominal value. In reality the remote | |
156 | * processor could be much slower to reply. So add an arbitrary amount | |
157 | * of wait on top of nominal. | |
158 | */ | |
159 | cl_info->deadline_us = | |
160 | HCCS_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency; | |
be2f78a8 HL |
161 | if (!hdev->verspec_data->has_txdone_irq && |
162 | cl_info->mbox_chan->mbox->txdone_irq) { | |
886bdf9c HL |
163 | dev_err(dev, "PCC IRQ in PCCT is enabled.\n"); |
164 | rc = -EINVAL; | |
165 | goto err_mbx_channel_free; | |
be2f78a8 HL |
166 | } else if (hdev->verspec_data->has_txdone_irq && |
167 | !cl_info->mbox_chan->mbox->txdone_irq) { | |
168 | dev_err(dev, "PCC IRQ in PCCT isn't supported.\n"); | |
169 | rc = -EINVAL; | |
170 | goto err_mbx_channel_free; | |
886bdf9c HL |
171 | } |
172 | ||
173 | if (pcc_chan->shmem_base_addr) { | |
7d661283 HL |
174 | cl_info->pcc_comm_addr = ioremap(pcc_chan->shmem_base_addr, |
175 | pcc_chan->shmem_size); | |
886bdf9c | 176 | if (!cl_info->pcc_comm_addr) { |
734add1a | 177 | dev_err(dev, "Failed to ioremap PCC communication region for channel-%u.\n", |
886bdf9c HL |
178 | hdev->chan_id); |
179 | rc = -ENOMEM; | |
180 | goto err_mbx_channel_free; | |
181 | } | |
182 | } | |
183 | ||
184 | return 0; | |
185 | ||
186 | err_mbx_channel_free: | |
187 | pcc_mbox_free_channel(cl_info->pcc_chan); | |
188 | out: | |
189 | return rc; | |
190 | } | |
191 | ||
be2f78a8 | 192 | static int hccs_wait_cmd_complete_by_poll(struct hccs_dev *hdev) |
886bdf9c HL |
193 | { |
194 | struct hccs_mbox_client_info *cl_info = &hdev->cl_info; | |
7d661283 HL |
195 | struct acpi_pcct_shared_memory __iomem *comm_base = |
196 | cl_info->pcc_comm_addr; | |
886bdf9c HL |
197 | u16 status; |
198 | int ret; | |
199 | ||
200 | /* | |
201 | * Poll PCC status register every 3us(delay_us) for maximum of | |
202 | * deadline_us(timeout_us) until PCC command complete bit is set(cond) | |
203 | */ | |
204 | ret = readw_poll_timeout(&comm_base->status, status, | |
a46e42c0 | 205 | status & PCC_STATUS_CMD_COMPLETE, |
886bdf9c HL |
206 | HCCS_POLL_STATUS_TIME_INTERVAL_US, |
207 | cl_info->deadline_us); | |
208 | if (unlikely(ret)) | |
209 | dev_err(hdev->dev, "poll PCC status failed, ret = %d.\n", ret); | |
210 | ||
211 | return ret; | |
212 | } | |
213 | ||
be2f78a8 HL |
214 | static int hccs_wait_cmd_complete_by_irq(struct hccs_dev *hdev) |
215 | { | |
216 | struct hccs_mbox_client_info *cl_info = &hdev->cl_info; | |
217 | ||
218 | if (!wait_for_completion_timeout(&cl_info->done, | |
219 | usecs_to_jiffies(cl_info->deadline_us))) { | |
220 | dev_err(hdev->dev, "PCC command executed timeout!\n"); | |
221 | return -ETIMEDOUT; | |
222 | } | |
223 | ||
224 | return 0; | |
225 | } | |
226 | ||
227 | static inline void hccs_fill_pcc_shared_mem_region(struct hccs_dev *hdev, | |
228 | u8 cmd, | |
229 | struct hccs_desc *desc, | |
230 | void __iomem *comm_space, | |
231 | u16 space_size) | |
232 | { | |
233 | struct acpi_pcct_shared_memory tmp = { | |
234 | .signature = PCC_SIGNATURE | hdev->chan_id, | |
235 | .command = cmd, | |
236 | .status = 0, | |
237 | }; | |
238 | ||
239 | memcpy_toio(hdev->cl_info.pcc_comm_addr, (void *)&tmp, | |
240 | sizeof(struct acpi_pcct_shared_memory)); | |
241 | ||
242 | /* Copy the message to the PCC comm space */ | |
243 | memcpy_toio(comm_space, (void *)desc, space_size); | |
244 | } | |
245 | ||
246 | static inline void hccs_fill_ext_pcc_shared_mem_region(struct hccs_dev *hdev, | |
247 | u8 cmd, | |
248 | struct hccs_desc *desc, | |
249 | void __iomem *comm_space, | |
250 | u16 space_size) | |
251 | { | |
252 | struct acpi_pcct_ext_pcc_shared_memory tmp = { | |
253 | .signature = PCC_SIGNATURE | hdev->chan_id, | |
254 | .flags = PCC_CMD_COMPLETION_NOTIFY, | |
255 | .length = HCCS_PCC_SHARE_MEM_BYTES, | |
256 | .command = cmd, | |
257 | }; | |
258 | ||
259 | memcpy_toio(hdev->cl_info.pcc_comm_addr, (void *)&tmp, | |
260 | sizeof(struct acpi_pcct_ext_pcc_shared_memory)); | |
261 | ||
262 | /* Copy the message to the PCC comm space */ | |
263 | memcpy_toio(comm_space, (void *)desc, space_size); | |
264 | } | |
265 | ||
886bdf9c HL |
266 | static int hccs_pcc_cmd_send(struct hccs_dev *hdev, u8 cmd, |
267 | struct hccs_desc *desc) | |
268 | { | |
be2f78a8 | 269 | const struct hccs_verspecific_data *verspec_data = hdev->verspec_data; |
886bdf9c | 270 | struct hccs_mbox_client_info *cl_info = &hdev->cl_info; |
886bdf9c | 271 | struct hccs_fw_inner_head *fw_inner_head; |
be2f78a8 HL |
272 | void __iomem *comm_space; |
273 | u16 space_size; | |
886bdf9c HL |
274 | int ret; |
275 | ||
be2f78a8 HL |
276 | comm_space = cl_info->pcc_comm_addr + verspec_data->shared_mem_size; |
277 | space_size = HCCS_PCC_SHARE_MEM_BYTES - verspec_data->shared_mem_size; | |
278 | verspec_data->fill_pcc_shared_mem(hdev, cmd, desc, | |
279 | comm_space, space_size); | |
280 | if (verspec_data->has_txdone_irq) | |
281 | reinit_completion(&cl_info->done); | |
886bdf9c HL |
282 | |
283 | /* Ring doorbell */ | |
284 | ret = mbox_send_message(cl_info->mbox_chan, &cmd); | |
285 | if (ret < 0) { | |
286 | dev_err(hdev->dev, "Send PCC mbox message failed, ret = %d.\n", | |
287 | ret); | |
288 | goto end; | |
289 | } | |
290 | ||
be2f78a8 | 291 | ret = verspec_data->wait_cmd_complete(hdev); |
886bdf9c HL |
292 | if (ret) |
293 | goto end; | |
294 | ||
295 | /* Copy response data */ | |
be2f78a8 | 296 | memcpy_fromio((void *)desc, comm_space, space_size); |
886bdf9c HL |
297 | fw_inner_head = &desc->rsp.fw_inner_head; |
298 | if (fw_inner_head->retStatus) { | |
299 | dev_err(hdev->dev, "Execute PCC command failed, error code = %u.\n", | |
300 | fw_inner_head->retStatus); | |
301 | ret = -EIO; | |
302 | } | |
303 | ||
304 | end: | |
be2f78a8 HL |
305 | if (verspec_data->has_txdone_irq) |
306 | mbox_chan_txdone(cl_info->mbox_chan, ret); | |
307 | else | |
308 | mbox_client_txdone(cl_info->mbox_chan, ret); | |
886bdf9c HL |
309 | return ret; |
310 | } | |
311 | ||
312 | static void hccs_init_req_desc(struct hccs_desc *desc) | |
313 | { | |
314 | struct hccs_req_desc *req = &desc->req; | |
315 | ||
316 | memset(desc, 0, sizeof(*desc)); | |
317 | req->req_head.module_code = HCCS_SERDES_MODULE_CODE; | |
318 | } | |
319 | ||
320 | static int hccs_get_dev_caps(struct hccs_dev *hdev) | |
321 | { | |
322 | struct hccs_desc desc; | |
323 | int ret; | |
324 | ||
325 | hccs_init_req_desc(&desc); | |
326 | ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DEV_CAP, &desc); | |
327 | if (ret) { | |
328 | dev_err(hdev->dev, "Get device capabilities failed, ret = %d.\n", | |
329 | ret); | |
330 | return ret; | |
331 | } | |
332 | memcpy(&hdev->caps, desc.rsp.data, sizeof(hdev->caps)); | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | static int hccs_query_chip_num_on_platform(struct hccs_dev *hdev) | |
338 | { | |
339 | struct hccs_desc desc; | |
340 | int ret; | |
341 | ||
342 | hccs_init_req_desc(&desc); | |
343 | ret = hccs_pcc_cmd_send(hdev, HCCS_GET_CHIP_NUM, &desc); | |
344 | if (ret) { | |
345 | dev_err(hdev->dev, "query system chip number failed, ret = %d.\n", | |
346 | ret); | |
347 | return ret; | |
348 | } | |
349 | ||
350 | hdev->chip_num = *((u8 *)&desc.rsp.data); | |
351 | if (!hdev->chip_num) { | |
352 | dev_err(hdev->dev, "chip num obtained from firmware is zero.\n"); | |
353 | return -EINVAL; | |
354 | } | |
355 | ||
356 | return 0; | |
357 | } | |
358 | ||
359 | static int hccs_get_chip_info(struct hccs_dev *hdev, | |
360 | struct hccs_chip_info *chip) | |
361 | { | |
362 | struct hccs_die_num_req_param *req_param; | |
363 | struct hccs_desc desc; | |
364 | int ret; | |
365 | ||
366 | hccs_init_req_desc(&desc); | |
367 | req_param = (struct hccs_die_num_req_param *)desc.req.data; | |
368 | req_param->chip_id = chip->chip_id; | |
369 | ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_NUM, &desc); | |
370 | if (ret) | |
371 | return ret; | |
372 | ||
373 | chip->die_num = *((u8 *)&desc.rsp.data); | |
374 | ||
375 | return 0; | |
376 | } | |
377 | ||
378 | static int hccs_query_chip_info_on_platform(struct hccs_dev *hdev) | |
379 | { | |
380 | struct hccs_chip_info *chip; | |
381 | int ret; | |
382 | u8 idx; | |
383 | ||
384 | ret = hccs_query_chip_num_on_platform(hdev); | |
385 | if (ret) { | |
386 | dev_err(hdev->dev, "query chip number on platform failed, ret = %d.\n", | |
387 | ret); | |
388 | return ret; | |
389 | } | |
390 | ||
391 | hdev->chips = devm_kzalloc(hdev->dev, | |
392 | hdev->chip_num * sizeof(struct hccs_chip_info), | |
393 | GFP_KERNEL); | |
394 | if (!hdev->chips) { | |
395 | dev_err(hdev->dev, "allocate all chips memory failed.\n"); | |
396 | return -ENOMEM; | |
397 | } | |
398 | ||
399 | for (idx = 0; idx < hdev->chip_num; idx++) { | |
400 | chip = &hdev->chips[idx]; | |
401 | chip->chip_id = idx; | |
402 | ret = hccs_get_chip_info(hdev, chip); | |
403 | if (ret) { | |
404 | dev_err(hdev->dev, "get chip%u info failed, ret = %d.\n", | |
405 | idx, ret); | |
406 | return ret; | |
407 | } | |
408 | chip->hdev = hdev; | |
409 | } | |
410 | ||
411 | return 0; | |
412 | } | |
413 | ||
414 | static int hccs_query_die_info_on_chip(struct hccs_dev *hdev, u8 chip_id, | |
415 | u8 die_idx, struct hccs_die_info *die) | |
416 | { | |
417 | struct hccs_die_info_req_param *req_param; | |
418 | struct hccs_die_info_rsp_data *rsp_data; | |
419 | struct hccs_desc desc; | |
420 | int ret; | |
421 | ||
422 | hccs_init_req_desc(&desc); | |
423 | req_param = (struct hccs_die_info_req_param *)desc.req.data; | |
424 | req_param->chip_id = chip_id; | |
425 | req_param->die_idx = die_idx; | |
426 | ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_INFO, &desc); | |
427 | if (ret) | |
428 | return ret; | |
429 | ||
430 | rsp_data = (struct hccs_die_info_rsp_data *)desc.rsp.data; | |
431 | die->die_id = rsp_data->die_id; | |
432 | die->port_num = rsp_data->port_num; | |
433 | die->min_port_id = rsp_data->min_port_id; | |
434 | die->max_port_id = rsp_data->max_port_id; | |
435 | if (die->min_port_id > die->max_port_id) { | |
436 | dev_err(hdev->dev, "min port id(%u) > max port id(%u) on die_idx(%u).\n", | |
437 | die->min_port_id, die->max_port_id, die_idx); | |
438 | return -EINVAL; | |
439 | } | |
440 | if (die->max_port_id > HCCS_DIE_MAX_PORT_ID) { | |
441 | dev_err(hdev->dev, "max port id(%u) on die_idx(%u) is too big.\n", | |
442 | die->max_port_id, die_idx); | |
443 | return -EINVAL; | |
444 | } | |
445 | ||
446 | return 0; | |
447 | } | |
448 | ||
449 | static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev) | |
450 | { | |
451 | struct device *dev = hdev->dev; | |
452 | struct hccs_chip_info *chip; | |
453 | struct hccs_die_info *die; | |
454 | u8 i, j; | |
455 | int ret; | |
456 | ||
457 | for (i = 0; i < hdev->chip_num; i++) { | |
458 | chip = &hdev->chips[i]; | |
459 | if (!chip->die_num) | |
460 | continue; | |
461 | ||
462 | chip->dies = devm_kzalloc(hdev->dev, | |
463 | chip->die_num * sizeof(struct hccs_die_info), | |
464 | GFP_KERNEL); | |
465 | if (!chip->dies) { | |
466 | dev_err(dev, "allocate all dies memory on chip%u failed.\n", | |
467 | i); | |
468 | return -ENOMEM; | |
469 | } | |
470 | ||
471 | for (j = 0; j < chip->die_num; j++) { | |
472 | die = &chip->dies[j]; | |
473 | ret = hccs_query_die_info_on_chip(hdev, i, j, die); | |
474 | if (ret) { | |
475 | dev_err(dev, "get die idx (%u) info on chip%u failed, ret = %d.\n", | |
476 | j, i, ret); | |
477 | return ret; | |
478 | } | |
479 | die->chip = chip; | |
480 | } | |
481 | } | |
482 | ||
483 | return 0; | |
484 | } | |
485 | ||
486 | static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode, | |
487 | struct hccs_desc *desc, | |
488 | void *buf, size_t buf_len, | |
489 | struct hccs_rsp_head *rsp_head) | |
490 | { | |
491 | struct hccs_rsp_head *head; | |
492 | struct hccs_rsp_desc *rsp; | |
493 | int ret; | |
494 | ||
495 | ret = hccs_pcc_cmd_send(hdev, opcode, desc); | |
496 | if (ret) | |
497 | return ret; | |
498 | ||
499 | rsp = &desc->rsp; | |
500 | head = &rsp->rsp_head; | |
501 | if (head->data_len > buf_len) { | |
502 | dev_err(hdev->dev, | |
37696fa7 | 503 | "buffer overflow (buf_len = %zu, data_len = %u)!\n", |
886bdf9c HL |
504 | buf_len, head->data_len); |
505 | return -ENOMEM; | |
506 | } | |
507 | ||
508 | memcpy(buf, rsp->data, head->data_len); | |
509 | *rsp_head = *head; | |
510 | ||
511 | return 0; | |
512 | } | |
513 | ||
514 | static int hccs_get_all_port_attr(struct hccs_dev *hdev, | |
515 | struct hccs_die_info *die, | |
516 | struct hccs_port_attr *attrs, u16 size) | |
517 | { | |
518 | struct hccs_die_comm_req_param *req_param; | |
519 | struct hccs_req_head *req_head; | |
520 | struct hccs_rsp_head rsp_head; | |
521 | struct hccs_desc desc; | |
522 | size_t left_buf_len; | |
523 | u32 data_len = 0; | |
524 | u8 start_id; | |
525 | u8 *buf; | |
526 | int ret; | |
527 | ||
528 | buf = (u8 *)attrs; | |
529 | left_buf_len = sizeof(struct hccs_port_attr) * size; | |
530 | start_id = die->min_port_id; | |
531 | while (start_id <= die->max_port_id) { | |
532 | hccs_init_req_desc(&desc); | |
533 | req_head = &desc.req.req_head; | |
534 | req_head->start_id = start_id; | |
535 | req_param = (struct hccs_die_comm_req_param *)desc.req.data; | |
536 | req_param->chip_id = die->chip->chip_id; | |
537 | req_param->die_id = die->die_id; | |
538 | ||
539 | ret = hccs_get_bd_info(hdev, HCCS_GET_DIE_PORT_INFO, &desc, | |
540 | buf + data_len, left_buf_len, &rsp_head); | |
541 | if (ret) { | |
542 | dev_err(hdev->dev, | |
543 | "get the information of port%u on die%u failed, ret = %d.\n", | |
544 | start_id, die->die_id, ret); | |
545 | return ret; | |
546 | } | |
547 | ||
548 | data_len += rsp_head.data_len; | |
549 | left_buf_len -= rsp_head.data_len; | |
550 | if (unlikely(rsp_head.next_id <= start_id)) { | |
551 | dev_err(hdev->dev, | |
552 | "next port id (%u) is not greater than last start id (%u) on die%u.\n", | |
553 | rsp_head.next_id, start_id, die->die_id); | |
554 | return -EINVAL; | |
555 | } | |
556 | start_id = rsp_head.next_id; | |
557 | } | |
558 | ||
192f97fe HL |
559 | if (left_buf_len != 0) { |
560 | dev_err(hdev->dev, "failed to get the expected port number(%u) attribute.\n", | |
561 | size); | |
562 | return -EINVAL; | |
563 | } | |
564 | ||
886bdf9c HL |
565 | return 0; |
566 | } | |
567 | ||
568 | static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev, | |
569 | struct hccs_die_info *die) | |
570 | { | |
571 | struct hccs_port_attr *attrs; | |
572 | struct hccs_port_info *port; | |
573 | int ret; | |
574 | u8 i; | |
575 | ||
576 | attrs = kcalloc(die->port_num, sizeof(struct hccs_port_attr), | |
577 | GFP_KERNEL); | |
578 | if (!attrs) | |
579 | return -ENOMEM; | |
580 | ||
581 | ret = hccs_get_all_port_attr(hdev, die, attrs, die->port_num); | |
582 | if (ret) | |
583 | goto out; | |
584 | ||
585 | for (i = 0; i < die->port_num; i++) { | |
586 | port = &die->ports[i]; | |
587 | port->port_id = attrs[i].port_id; | |
588 | port->port_type = attrs[i].port_type; | |
589 | port->lane_mode = attrs[i].lane_mode; | |
590 | port->enable = attrs[i].enable; | |
591 | port->die = die; | |
592 | } | |
593 | ||
594 | out: | |
595 | kfree(attrs); | |
596 | return ret; | |
597 | } | |
598 | ||
599 | static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev) | |
600 | { | |
886bdf9c HL |
601 | struct device *dev = hdev->dev; |
602 | struct hccs_chip_info *chip; | |
603 | struct hccs_die_info *die; | |
604 | u8 i, j; | |
605 | int ret; | |
606 | ||
607 | for (i = 0; i < hdev->chip_num; i++) { | |
608 | chip = &hdev->chips[i]; | |
609 | for (j = 0; j < chip->die_num; j++) { | |
610 | die = &chip->dies[j]; | |
611 | if (!die->port_num) | |
612 | continue; | |
613 | ||
614 | die->ports = devm_kzalloc(dev, | |
615 | die->port_num * sizeof(struct hccs_port_info), | |
616 | GFP_KERNEL); | |
617 | if (!die->ports) { | |
618 | dev_err(dev, "allocate ports memory on chip%u/die%u failed.\n", | |
619 | i, die->die_id); | |
620 | return -ENOMEM; | |
621 | } | |
622 | ||
623 | ret = hccs_get_all_port_info_on_die(hdev, die); | |
624 | if (ret) { | |
625 | dev_err(dev, "get all port info on chip%u/die%u failed, ret = %d.\n", | |
626 | i, die->die_id, ret); | |
627 | return ret; | |
628 | } | |
629 | } | |
630 | } | |
631 | ||
632 | return 0; | |
633 | } | |
634 | ||
635 | static int hccs_get_hw_info(struct hccs_dev *hdev) | |
636 | { | |
637 | int ret; | |
638 | ||
639 | ret = hccs_query_chip_info_on_platform(hdev); | |
640 | if (ret) { | |
641 | dev_err(hdev->dev, "query chip info on platform failed, ret = %d.\n", | |
642 | ret); | |
643 | return ret; | |
644 | } | |
645 | ||
646 | ret = hccs_query_all_die_info_on_platform(hdev); | |
647 | if (ret) { | |
648 | dev_err(hdev->dev, "query all die info on platform failed, ret = %d.\n", | |
649 | ret); | |
650 | return ret; | |
651 | } | |
652 | ||
653 | ret = hccs_query_all_port_info_on_platform(hdev); | |
654 | if (ret) { | |
655 | dev_err(hdev->dev, "query all port info on platform failed, ret = %d.\n", | |
656 | ret); | |
657 | return ret; | |
658 | } | |
659 | ||
660 | return 0; | |
661 | } | |
662 | ||
47f7a255 HL |
663 | static int hccs_query_port_link_status(struct hccs_dev *hdev, |
664 | const struct hccs_port_info *port, | |
665 | struct hccs_link_status *link_status) | |
666 | { | |
667 | const struct hccs_die_info *die = port->die; | |
668 | const struct hccs_chip_info *chip = die->chip; | |
669 | struct hccs_port_comm_req_param *req_param; | |
670 | struct hccs_desc desc; | |
671 | int ret; | |
672 | ||
673 | hccs_init_req_desc(&desc); | |
674 | req_param = (struct hccs_port_comm_req_param *)desc.req.data; | |
675 | req_param->chip_id = chip->chip_id; | |
676 | req_param->die_id = die->die_id; | |
677 | req_param->port_id = port->port_id; | |
678 | ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_LINK_STATUS, &desc); | |
679 | if (ret) { | |
680 | dev_err(hdev->dev, | |
681 | "get port link status info failed, ret = %d.\n", ret); | |
682 | return ret; | |
683 | } | |
684 | ||
685 | *link_status = *((struct hccs_link_status *)desc.rsp.data); | |
686 | ||
687 | return 0; | |
688 | } | |
689 | ||
690 | static int hccs_query_port_crc_err_cnt(struct hccs_dev *hdev, | |
691 | const struct hccs_port_info *port, | |
692 | u64 *crc_err_cnt) | |
693 | { | |
694 | const struct hccs_die_info *die = port->die; | |
695 | const struct hccs_chip_info *chip = die->chip; | |
696 | struct hccs_port_comm_req_param *req_param; | |
697 | struct hccs_desc desc; | |
698 | int ret; | |
699 | ||
700 | hccs_init_req_desc(&desc); | |
701 | req_param = (struct hccs_port_comm_req_param *)desc.req.data; | |
702 | req_param->chip_id = chip->chip_id; | |
703 | req_param->die_id = die->die_id; | |
704 | req_param->port_id = port->port_id; | |
705 | ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_CRC_ERR_CNT, &desc); | |
706 | if (ret) { | |
707 | dev_err(hdev->dev, | |
708 | "get port crc error count failed, ret = %d.\n", ret); | |
709 | return ret; | |
710 | } | |
711 | ||
712 | memcpy(crc_err_cnt, &desc.rsp.data, sizeof(u64)); | |
713 | ||
714 | return 0; | |
715 | } | |
716 | ||
717 | static int hccs_get_die_all_link_status(struct hccs_dev *hdev, | |
718 | const struct hccs_die_info *die, | |
719 | u8 *all_linked) | |
720 | { | |
721 | struct hccs_die_comm_req_param *req_param; | |
722 | struct hccs_desc desc; | |
723 | int ret; | |
724 | ||
725 | if (die->port_num == 0) { | |
726 | *all_linked = 1; | |
727 | return 0; | |
728 | } | |
729 | ||
730 | hccs_init_req_desc(&desc); | |
731 | req_param = (struct hccs_die_comm_req_param *)desc.req.data; | |
732 | req_param->chip_id = die->chip->chip_id; | |
733 | req_param->die_id = die->die_id; | |
734 | ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LINK_STA, &desc); | |
735 | if (ret) { | |
736 | dev_err(hdev->dev, | |
737 | "get link status of all ports failed on die%u, ret = %d.\n", | |
738 | die->die_id, ret); | |
739 | return ret; | |
740 | } | |
741 | ||
742 | *all_linked = *((u8 *)&desc.rsp.data); | |
743 | ||
744 | return 0; | |
745 | } | |
746 | ||
747 | static int hccs_get_die_all_port_lane_status(struct hccs_dev *hdev, | |
748 | const struct hccs_die_info *die, | |
749 | u8 *full_lane) | |
750 | { | |
751 | struct hccs_die_comm_req_param *req_param; | |
752 | struct hccs_desc desc; | |
753 | int ret; | |
754 | ||
755 | if (die->port_num == 0) { | |
756 | *full_lane = 1; | |
757 | return 0; | |
758 | } | |
759 | ||
760 | hccs_init_req_desc(&desc); | |
761 | req_param = (struct hccs_die_comm_req_param *)desc.req.data; | |
762 | req_param->chip_id = die->chip->chip_id; | |
763 | req_param->die_id = die->die_id; | |
764 | ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_LANE_STA, &desc); | |
765 | if (ret) { | |
766 | dev_err(hdev->dev, "get lane status of all ports failed on die%u, ret = %d.\n", | |
767 | die->die_id, ret); | |
768 | return ret; | |
769 | } | |
770 | ||
771 | *full_lane = *((u8 *)&desc.rsp.data); | |
772 | ||
773 | return 0; | |
774 | } | |
775 | ||
776 | static int hccs_get_die_total_crc_err_cnt(struct hccs_dev *hdev, | |
777 | const struct hccs_die_info *die, | |
778 | u64 *total_crc_err_cnt) | |
779 | { | |
780 | struct hccs_die_comm_req_param *req_param; | |
781 | struct hccs_desc desc; | |
782 | int ret; | |
783 | ||
784 | if (die->port_num == 0) { | |
785 | *total_crc_err_cnt = 0; | |
786 | return 0; | |
787 | } | |
788 | ||
789 | hccs_init_req_desc(&desc); | |
790 | req_param = (struct hccs_die_comm_req_param *)desc.req.data; | |
791 | req_param->chip_id = die->chip->chip_id; | |
792 | req_param->die_id = die->die_id; | |
793 | ret = hccs_pcc_cmd_send(hdev, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, &desc); | |
794 | if (ret) { | |
795 | dev_err(hdev->dev, "get crc error count sum failed on die%u, ret = %d.\n", | |
796 | die->die_id, ret); | |
797 | return ret; | |
798 | } | |
799 | ||
800 | memcpy(total_crc_err_cnt, &desc.rsp.data, sizeof(u64)); | |
801 | ||
802 | return 0; | |
803 | } | |
804 | ||
805 | static ssize_t hccs_show(struct kobject *k, struct attribute *attr, char *buf) | |
806 | { | |
807 | struct kobj_attribute *kobj_attr; | |
808 | ||
809 | kobj_attr = container_of(attr, struct kobj_attribute, attr); | |
810 | ||
811 | return kobj_attr->show(k, kobj_attr, buf); | |
812 | } | |
813 | ||
814 | static const struct sysfs_ops hccs_comm_ops = { | |
815 | .show = hccs_show, | |
816 | }; | |
817 | ||
818 | static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, | |
819 | char *buf) | |
820 | { | |
821 | const struct hccs_port_info *port = kobj_to_port_info(kobj); | |
822 | ||
823 | return sysfs_emit(buf, "HCCS-v%u\n", port->port_type); | |
824 | } | |
825 | static struct kobj_attribute hccs_type_attr = __ATTR_RO(type); | |
826 | ||
827 | static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr, | |
828 | char *buf) | |
829 | { | |
830 | const struct hccs_port_info *port = kobj_to_port_info(kobj); | |
831 | ||
832 | return sysfs_emit(buf, "x%u\n", port->lane_mode); | |
833 | } | |
834 | static struct kobj_attribute lane_mode_attr = __ATTR_RO(lane_mode); | |
835 | ||
836 | static ssize_t enable_show(struct kobject *kobj, | |
837 | struct kobj_attribute *attr, char *buf) | |
838 | { | |
839 | const struct hccs_port_info *port = kobj_to_port_info(kobj); | |
840 | ||
841 | return sysfs_emit(buf, "%u\n", port->enable); | |
842 | } | |
843 | static struct kobj_attribute port_enable_attr = __ATTR_RO(enable); | |
844 | ||
845 | static ssize_t cur_lane_num_show(struct kobject *kobj, | |
846 | struct kobj_attribute *attr, char *buf) | |
847 | { | |
848 | const struct hccs_port_info *port = kobj_to_port_info(kobj); | |
849 | struct hccs_dev *hdev = port->die->chip->hdev; | |
850 | struct hccs_link_status link_status = {0}; | |
851 | int ret; | |
852 | ||
853 | mutex_lock(&hdev->lock); | |
854 | ret = hccs_query_port_link_status(hdev, port, &link_status); | |
855 | mutex_unlock(&hdev->lock); | |
856 | if (ret) | |
857 | return ret; | |
858 | ||
859 | return sysfs_emit(buf, "%u\n", link_status.lane_num); | |
860 | } | |
861 | static struct kobj_attribute cur_lane_num_attr = __ATTR_RO(cur_lane_num); | |
862 | ||
863 | static ssize_t link_fsm_show(struct kobject *kobj, | |
864 | struct kobj_attribute *attr, char *buf) | |
865 | { | |
866 | const struct hccs_port_info *port = kobj_to_port_info(kobj); | |
867 | struct hccs_dev *hdev = port->die->chip->hdev; | |
868 | struct hccs_link_status link_status = {0}; | |
869 | const struct { | |
870 | u8 link_fsm; | |
871 | char *str; | |
872 | } link_fsm_map[] = { | |
873 | {HCCS_PORT_RESET, "reset"}, | |
874 | {HCCS_PORT_SETUP, "setup"}, | |
875 | {HCCS_PORT_CONFIG, "config"}, | |
876 | {HCCS_PORT_READY, "link-up"}, | |
877 | }; | |
878 | const char *link_fsm_str = "unknown"; | |
879 | size_t i; | |
880 | int ret; | |
881 | ||
882 | mutex_lock(&hdev->lock); | |
883 | ret = hccs_query_port_link_status(hdev, port, &link_status); | |
884 | mutex_unlock(&hdev->lock); | |
885 | if (ret) | |
886 | return ret; | |
887 | ||
888 | for (i = 0; i < ARRAY_SIZE(link_fsm_map); i++) { | |
889 | if (link_fsm_map[i].link_fsm == link_status.link_fsm) { | |
890 | link_fsm_str = link_fsm_map[i].str; | |
891 | break; | |
892 | } | |
893 | } | |
894 | ||
895 | return sysfs_emit(buf, "%s\n", link_fsm_str); | |
896 | } | |
897 | static struct kobj_attribute link_fsm_attr = __ATTR_RO(link_fsm); | |
898 | ||
899 | static ssize_t lane_mask_show(struct kobject *kobj, | |
900 | struct kobj_attribute *attr, char *buf) | |
901 | { | |
902 | const struct hccs_port_info *port = kobj_to_port_info(kobj); | |
903 | struct hccs_dev *hdev = port->die->chip->hdev; | |
904 | struct hccs_link_status link_status = {0}; | |
905 | int ret; | |
906 | ||
907 | mutex_lock(&hdev->lock); | |
908 | ret = hccs_query_port_link_status(hdev, port, &link_status); | |
909 | mutex_unlock(&hdev->lock); | |
910 | if (ret) | |
911 | return ret; | |
912 | ||
913 | return sysfs_emit(buf, "0x%x\n", link_status.lane_mask); | |
914 | } | |
915 | static struct kobj_attribute lane_mask_attr = __ATTR_RO(lane_mask); | |
916 | ||
917 | static ssize_t crc_err_cnt_show(struct kobject *kobj, | |
918 | struct kobj_attribute *attr, char *buf) | |
919 | { | |
920 | const struct hccs_port_info *port = kobj_to_port_info(kobj); | |
921 | struct hccs_dev *hdev = port->die->chip->hdev; | |
922 | u64 crc_err_cnt; | |
923 | int ret; | |
924 | ||
925 | mutex_lock(&hdev->lock); | |
926 | ret = hccs_query_port_crc_err_cnt(hdev, port, &crc_err_cnt); | |
927 | mutex_unlock(&hdev->lock); | |
928 | if (ret) | |
929 | return ret; | |
930 | ||
931 | return sysfs_emit(buf, "%llu\n", crc_err_cnt); | |
932 | } | |
933 | static struct kobj_attribute crc_err_cnt_attr = __ATTR_RO(crc_err_cnt); | |
934 | ||
935 | static struct attribute *hccs_port_default_attrs[] = { | |
936 | &hccs_type_attr.attr, | |
937 | &lane_mode_attr.attr, | |
938 | &port_enable_attr.attr, | |
939 | &cur_lane_num_attr.attr, | |
940 | &link_fsm_attr.attr, | |
941 | &lane_mask_attr.attr, | |
942 | &crc_err_cnt_attr.attr, | |
943 | NULL, | |
944 | }; | |
945 | ATTRIBUTE_GROUPS(hccs_port_default); | |
946 | ||
947 | static const struct kobj_type hccs_port_type = { | |
948 | .sysfs_ops = &hccs_comm_ops, | |
949 | .default_groups = hccs_port_default_groups, | |
950 | }; | |
951 | ||
952 | static ssize_t all_linked_on_die_show(struct kobject *kobj, | |
953 | struct kobj_attribute *attr, char *buf) | |
954 | { | |
955 | const struct hccs_die_info *die = kobj_to_die_info(kobj); | |
956 | struct hccs_dev *hdev = die->chip->hdev; | |
957 | u8 all_linked; | |
958 | int ret; | |
959 | ||
960 | mutex_lock(&hdev->lock); | |
961 | ret = hccs_get_die_all_link_status(hdev, die, &all_linked); | |
962 | mutex_unlock(&hdev->lock); | |
963 | if (ret) | |
964 | return ret; | |
965 | ||
966 | return sysfs_emit(buf, "%u\n", all_linked); | |
967 | } | |
968 | static struct kobj_attribute all_linked_on_die_attr = | |
969 | __ATTR(all_linked, 0444, all_linked_on_die_show, NULL); | |
970 | ||
971 | static ssize_t linked_full_lane_on_die_show(struct kobject *kobj, | |
972 | struct kobj_attribute *attr, | |
973 | char *buf) | |
974 | { | |
975 | const struct hccs_die_info *die = kobj_to_die_info(kobj); | |
976 | struct hccs_dev *hdev = die->chip->hdev; | |
977 | u8 full_lane; | |
978 | int ret; | |
979 | ||
980 | mutex_lock(&hdev->lock); | |
981 | ret = hccs_get_die_all_port_lane_status(hdev, die, &full_lane); | |
982 | mutex_unlock(&hdev->lock); | |
983 | if (ret) | |
984 | return ret; | |
985 | ||
986 | return sysfs_emit(buf, "%u\n", full_lane); | |
987 | } | |
988 | static struct kobj_attribute linked_full_lane_on_die_attr = | |
989 | __ATTR(linked_full_lane, 0444, linked_full_lane_on_die_show, NULL); | |
990 | ||
991 | static ssize_t crc_err_cnt_sum_on_die_show(struct kobject *kobj, | |
992 | struct kobj_attribute *attr, | |
993 | char *buf) | |
994 | { | |
995 | const struct hccs_die_info *die = kobj_to_die_info(kobj); | |
996 | struct hccs_dev *hdev = die->chip->hdev; | |
997 | u64 total_crc_err_cnt; | |
998 | int ret; | |
999 | ||
1000 | mutex_lock(&hdev->lock); | |
1001 | ret = hccs_get_die_total_crc_err_cnt(hdev, die, &total_crc_err_cnt); | |
1002 | mutex_unlock(&hdev->lock); | |
1003 | if (ret) | |
1004 | return ret; | |
1005 | ||
1006 | return sysfs_emit(buf, "%llu\n", total_crc_err_cnt); | |
1007 | } | |
1008 | static struct kobj_attribute crc_err_cnt_sum_on_die_attr = | |
1009 | __ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_die_show, NULL); | |
1010 | ||
1011 | static struct attribute *hccs_die_default_attrs[] = { | |
1012 | &all_linked_on_die_attr.attr, | |
1013 | &linked_full_lane_on_die_attr.attr, | |
1014 | &crc_err_cnt_sum_on_die_attr.attr, | |
1015 | NULL, | |
1016 | }; | |
1017 | ATTRIBUTE_GROUPS(hccs_die_default); | |
1018 | ||
1019 | static const struct kobj_type hccs_die_type = { | |
1020 | .sysfs_ops = &hccs_comm_ops, | |
1021 | .default_groups = hccs_die_default_groups, | |
1022 | }; | |
1023 | ||
1024 | static ssize_t all_linked_on_chip_show(struct kobject *kobj, | |
1025 | struct kobj_attribute *attr, char *buf) | |
1026 | { | |
1027 | const struct hccs_chip_info *chip = kobj_to_chip_info(kobj); | |
1028 | struct hccs_dev *hdev = chip->hdev; | |
1029 | const struct hccs_die_info *die; | |
1030 | u8 all_linked = 1; | |
1031 | u8 i, tmp; | |
1032 | int ret; | |
1033 | ||
1034 | mutex_lock(&hdev->lock); | |
1035 | for (i = 0; i < chip->die_num; i++) { | |
1036 | die = &chip->dies[i]; | |
1037 | ret = hccs_get_die_all_link_status(hdev, die, &tmp); | |
1038 | if (ret) { | |
1039 | mutex_unlock(&hdev->lock); | |
1040 | return ret; | |
1041 | } | |
1042 | if (tmp != all_linked) { | |
1043 | all_linked = 0; | |
1044 | break; | |
1045 | } | |
1046 | } | |
1047 | mutex_unlock(&hdev->lock); | |
1048 | ||
1049 | return sysfs_emit(buf, "%u\n", all_linked); | |
1050 | } | |
1051 | static struct kobj_attribute all_linked_on_chip_attr = | |
1052 | __ATTR(all_linked, 0444, all_linked_on_chip_show, NULL); | |
1053 | ||
1054 | static ssize_t linked_full_lane_on_chip_show(struct kobject *kobj, | |
1055 | struct kobj_attribute *attr, | |
1056 | char *buf) | |
1057 | { | |
1058 | const struct hccs_chip_info *chip = kobj_to_chip_info(kobj); | |
1059 | struct hccs_dev *hdev = chip->hdev; | |
1060 | const struct hccs_die_info *die; | |
1061 | u8 full_lane = 1; | |
1062 | u8 i, tmp; | |
1063 | int ret; | |
1064 | ||
1065 | mutex_lock(&hdev->lock); | |
1066 | for (i = 0; i < chip->die_num; i++) { | |
1067 | die = &chip->dies[i]; | |
1068 | ret = hccs_get_die_all_port_lane_status(hdev, die, &tmp); | |
1069 | if (ret) { | |
1070 | mutex_unlock(&hdev->lock); | |
1071 | return ret; | |
1072 | } | |
1073 | if (tmp != full_lane) { | |
1074 | full_lane = 0; | |
1075 | break; | |
1076 | } | |
1077 | } | |
1078 | mutex_unlock(&hdev->lock); | |
1079 | ||
1080 | return sysfs_emit(buf, "%u\n", full_lane); | |
1081 | } | |
1082 | static struct kobj_attribute linked_full_lane_on_chip_attr = | |
1083 | __ATTR(linked_full_lane, 0444, linked_full_lane_on_chip_show, NULL); | |
1084 | ||
1085 | static ssize_t crc_err_cnt_sum_on_chip_show(struct kobject *kobj, | |
1086 | struct kobj_attribute *attr, | |
1087 | char *buf) | |
1088 | { | |
1089 | const struct hccs_chip_info *chip = kobj_to_chip_info(kobj); | |
1090 | u64 crc_err_cnt, total_crc_err_cnt = 0; | |
1091 | struct hccs_dev *hdev = chip->hdev; | |
1092 | const struct hccs_die_info *die; | |
1093 | int ret; | |
1094 | u16 i; | |
1095 | ||
1096 | mutex_lock(&hdev->lock); | |
1097 | for (i = 0; i < chip->die_num; i++) { | |
1098 | die = &chip->dies[i]; | |
1099 | ret = hccs_get_die_total_crc_err_cnt(hdev, die, &crc_err_cnt); | |
1100 | if (ret) { | |
1101 | mutex_unlock(&hdev->lock); | |
1102 | return ret; | |
1103 | } | |
1104 | ||
1105 | total_crc_err_cnt += crc_err_cnt; | |
1106 | } | |
1107 | mutex_unlock(&hdev->lock); | |
1108 | ||
1109 | return sysfs_emit(buf, "%llu\n", total_crc_err_cnt); | |
1110 | } | |
1111 | static struct kobj_attribute crc_err_cnt_sum_on_chip_attr = | |
1112 | __ATTR(crc_err_cnt, 0444, crc_err_cnt_sum_on_chip_show, NULL); | |
1113 | ||
1114 | static struct attribute *hccs_chip_default_attrs[] = { | |
1115 | &all_linked_on_chip_attr.attr, | |
1116 | &linked_full_lane_on_chip_attr.attr, | |
1117 | &crc_err_cnt_sum_on_chip_attr.attr, | |
1118 | NULL, | |
1119 | }; | |
1120 | ATTRIBUTE_GROUPS(hccs_chip_default); | |
1121 | ||
1122 | static const struct kobj_type hccs_chip_type = { | |
1123 | .sysfs_ops = &hccs_comm_ops, | |
1124 | .default_groups = hccs_chip_default_groups, | |
1125 | }; | |
1126 | ||
1127 | static void hccs_remove_die_dir(struct hccs_die_info *die) | |
1128 | { | |
1129 | struct hccs_port_info *port; | |
1130 | u8 i; | |
1131 | ||
1132 | for (i = 0; i < die->port_num; i++) { | |
1133 | port = &die->ports[i]; | |
1134 | if (port->dir_created) | |
1135 | kobject_put(&port->kobj); | |
1136 | } | |
1137 | ||
1138 | kobject_put(&die->kobj); | |
1139 | } | |
1140 | ||
1141 | static void hccs_remove_chip_dir(struct hccs_chip_info *chip) | |
1142 | { | |
1143 | struct hccs_die_info *die; | |
1144 | u8 i; | |
1145 | ||
1146 | for (i = 0; i < chip->die_num; i++) { | |
1147 | die = &chip->dies[i]; | |
1148 | if (die->dir_created) | |
1149 | hccs_remove_die_dir(die); | |
1150 | } | |
1151 | ||
1152 | kobject_put(&chip->kobj); | |
1153 | } | |
1154 | ||
1155 | static void hccs_remove_topo_dirs(struct hccs_dev *hdev) | |
1156 | { | |
1157 | u8 i; | |
1158 | ||
1159 | for (i = 0; i < hdev->chip_num; i++) | |
1160 | hccs_remove_chip_dir(&hdev->chips[i]); | |
1161 | } | |
1162 | ||
1163 | static int hccs_create_hccs_dir(struct hccs_dev *hdev, | |
1164 | struct hccs_die_info *die, | |
1165 | struct hccs_port_info *port) | |
1166 | { | |
1167 | int ret; | |
1168 | ||
1169 | ret = kobject_init_and_add(&port->kobj, &hccs_port_type, | |
734add1a | 1170 | &die->kobj, "hccs%u", port->port_id); |
47f7a255 HL |
1171 | if (ret) { |
1172 | kobject_put(&port->kobj); | |
1173 | return ret; | |
1174 | } | |
1175 | ||
1176 | return 0; | |
1177 | } | |
1178 | ||
1179 | static int hccs_create_die_dir(struct hccs_dev *hdev, | |
1180 | struct hccs_chip_info *chip, | |
1181 | struct hccs_die_info *die) | |
1182 | { | |
1183 | struct hccs_port_info *port; | |
1184 | int ret; | |
1185 | u16 i; | |
1186 | ||
1187 | ret = kobject_init_and_add(&die->kobj, &hccs_die_type, | |
734add1a | 1188 | &chip->kobj, "die%u", die->die_id); |
47f7a255 HL |
1189 | if (ret) { |
1190 | kobject_put(&die->kobj); | |
1191 | return ret; | |
1192 | } | |
1193 | ||
1194 | for (i = 0; i < die->port_num; i++) { | |
1195 | port = &die->ports[i]; | |
1196 | ret = hccs_create_hccs_dir(hdev, die, port); | |
1197 | if (ret) { | |
734add1a | 1198 | dev_err(hdev->dev, "create hccs%u dir failed.\n", |
47f7a255 HL |
1199 | port->port_id); |
1200 | goto err; | |
1201 | } | |
1202 | port->dir_created = true; | |
1203 | } | |
1204 | ||
1205 | return 0; | |
1206 | err: | |
1207 | hccs_remove_die_dir(die); | |
1208 | ||
1209 | return ret; | |
1210 | } | |
1211 | ||
1212 | static int hccs_create_chip_dir(struct hccs_dev *hdev, | |
1213 | struct hccs_chip_info *chip) | |
1214 | { | |
1215 | struct hccs_die_info *die; | |
1216 | int ret; | |
1217 | u16 id; | |
1218 | ||
1219 | ret = kobject_init_and_add(&chip->kobj, &hccs_chip_type, | |
734add1a | 1220 | &hdev->dev->kobj, "chip%u", chip->chip_id); |
47f7a255 HL |
1221 | if (ret) { |
1222 | kobject_put(&chip->kobj); | |
1223 | return ret; | |
1224 | } | |
1225 | ||
1226 | for (id = 0; id < chip->die_num; id++) { | |
1227 | die = &chip->dies[id]; | |
1228 | ret = hccs_create_die_dir(hdev, chip, die); | |
1229 | if (ret) | |
1230 | goto err; | |
1231 | die->dir_created = true; | |
1232 | } | |
1233 | ||
1234 | return 0; | |
1235 | err: | |
1236 | hccs_remove_chip_dir(chip); | |
1237 | ||
1238 | return ret; | |
1239 | } | |
1240 | ||
1241 | static int hccs_create_topo_dirs(struct hccs_dev *hdev) | |
1242 | { | |
1243 | struct hccs_chip_info *chip; | |
1244 | u8 id, k; | |
1245 | int ret; | |
1246 | ||
1247 | for (id = 0; id < hdev->chip_num; id++) { | |
1248 | chip = &hdev->chips[id]; | |
1249 | ret = hccs_create_chip_dir(hdev, chip); | |
1250 | if (ret) { | |
734add1a | 1251 | dev_err(hdev->dev, "init chip%u dir failed!\n", id); |
47f7a255 HL |
1252 | goto err; |
1253 | } | |
1254 | } | |
1255 | ||
1256 | return 0; | |
1257 | err: | |
1258 | for (k = 0; k < id; k++) | |
1259 | hccs_remove_chip_dir(&hdev->chips[k]); | |
1260 | ||
1261 | return ret; | |
1262 | } | |
1263 | ||
886bdf9c HL |
1264 | static int hccs_probe(struct platform_device *pdev) |
1265 | { | |
1266 | struct acpi_device *acpi_dev; | |
1267 | struct hccs_dev *hdev; | |
1268 | int rc; | |
1269 | ||
1270 | if (acpi_disabled) { | |
1271 | dev_err(&pdev->dev, "acpi is disabled.\n"); | |
1272 | return -ENODEV; | |
1273 | } | |
1274 | acpi_dev = ACPI_COMPANION(&pdev->dev); | |
1275 | if (!acpi_dev) | |
1276 | return -ENODEV; | |
1277 | ||
1278 | hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL); | |
1279 | if (!hdev) | |
1280 | return -ENOMEM; | |
1281 | hdev->acpi_dev = acpi_dev; | |
1282 | hdev->dev = &pdev->dev; | |
1283 | platform_set_drvdata(pdev, hdev); | |
1284 | ||
be2f78a8 HL |
1285 | /* |
1286 | * Here would never be failure as the driver and device has been matched. | |
1287 | */ | |
1288 | hdev->verspec_data = acpi_device_get_match_data(hdev->dev); | |
1289 | ||
886bdf9c HL |
1290 | mutex_init(&hdev->lock); |
1291 | rc = hccs_get_pcc_chan_id(hdev); | |
1292 | if (rc) | |
1293 | return rc; | |
1294 | rc = hccs_register_pcc_channel(hdev); | |
1295 | if (rc) | |
1296 | return rc; | |
1297 | ||
1298 | rc = hccs_get_dev_caps(hdev); | |
1299 | if (rc) | |
1300 | goto unregister_pcc_chan; | |
1301 | ||
1302 | rc = hccs_get_hw_info(hdev); | |
1303 | if (rc) | |
1304 | goto unregister_pcc_chan; | |
1305 | ||
47f7a255 HL |
1306 | rc = hccs_create_topo_dirs(hdev); |
1307 | if (rc) | |
1308 | goto unregister_pcc_chan; | |
1309 | ||
886bdf9c HL |
1310 | return 0; |
1311 | ||
1312 | unregister_pcc_chan: | |
1313 | hccs_unregister_pcc_channel(hdev); | |
1314 | ||
1315 | return rc; | |
1316 | } | |
1317 | ||
3f40c999 | 1318 | static void hccs_remove(struct platform_device *pdev) |
886bdf9c HL |
1319 | { |
1320 | struct hccs_dev *hdev = platform_get_drvdata(pdev); | |
1321 | ||
47f7a255 | 1322 | hccs_remove_topo_dirs(hdev); |
886bdf9c | 1323 | hccs_unregister_pcc_channel(hdev); |
886bdf9c HL |
1324 | } |
1325 | ||
be2f78a8 HL |
1326 | static const struct hccs_verspecific_data hisi04b1_verspec_data = { |
1327 | .rx_callback = NULL, | |
1328 | .wait_cmd_complete = hccs_wait_cmd_complete_by_poll, | |
1329 | .fill_pcc_shared_mem = hccs_fill_pcc_shared_mem_region, | |
1330 | .shared_mem_size = sizeof(struct acpi_pcct_shared_memory), | |
1331 | .has_txdone_irq = false, | |
1332 | }; | |
1333 | ||
1334 | static const struct hccs_verspecific_data hisi04b2_verspec_data = { | |
1335 | .rx_callback = hccs_pcc_rx_callback, | |
1336 | .wait_cmd_complete = hccs_wait_cmd_complete_by_irq, | |
1337 | .fill_pcc_shared_mem = hccs_fill_ext_pcc_shared_mem_region, | |
1338 | .shared_mem_size = sizeof(struct acpi_pcct_ext_pcc_shared_memory), | |
1339 | .has_txdone_irq = true, | |
1340 | }; | |
1341 | ||
886bdf9c | 1342 | static const struct acpi_device_id hccs_acpi_match[] = { |
be2f78a8 HL |
1343 | { "HISI04B1", (unsigned long)&hisi04b1_verspec_data}, |
1344 | { "HISI04B2", (unsigned long)&hisi04b2_verspec_data}, | |
1345 | { } | |
886bdf9c HL |
1346 | }; |
1347 | MODULE_DEVICE_TABLE(acpi, hccs_acpi_match); | |
1348 | ||
1349 | static struct platform_driver hccs_driver = { | |
1350 | .probe = hccs_probe, | |
3f40c999 | 1351 | .remove_new = hccs_remove, |
886bdf9c HL |
1352 | .driver = { |
1353 | .name = "kunpeng_hccs", | |
1354 | .acpi_match_table = hccs_acpi_match, | |
1355 | }, | |
1356 | }; | |
1357 | ||
1358 | module_platform_driver(hccs_driver); | |
1359 | ||
1360 | MODULE_DESCRIPTION("Kunpeng SoC HCCS driver"); | |
1361 | MODULE_LICENSE("GPL"); | |
1362 | MODULE_AUTHOR("Huisong Li <lihuisong@huawei.com>"); |