On Tue, 9 Jul 2024 17:34:18 +0800 Xiaofei Tan tanxiaofei@huawei.com wrote:
Kunpeng SoC support several memory repair capability. Normally, such repairs are done in firmware startup stage or triggered by BMC(Baseboard Management Controller) and done in firmware in runtime stage. OS can't perceive such capability.
Some support online repair, while others can't. One reason of not supporting online repair is that the memory is in use and the hardware does not support backpressure on the primary access channel.
In order to support more online memory repair, we seek solutions from OS. That is try to isolate the memory page first, and do repair, and de-isloate after repair done.
This patch support online ACLS(Adapter Cache Line Sparing) repair, and may add other repair such as PPR in future.
Signed-off-by: Xiaofei Tan tanxiaofei@huawei.com
As you know PPR is on the list of features we are proposing to cover with the 'new' EDAC stuff / RAS control that Shiju is working on so as to bring a unified interface to CXL and anything else (e.g. this) that needs it. (I happen to know there is a proposal from Micron to discuss PPR specifically at LPC).
I'm not keen to see an upstream solution for a specific device that we then have to move over later.
More significantly what stops the user of this shooting themselves in the foot? How do we know the memory is offline when repair is attempted? That was the bit that I was least sure about for the CXL version (and why it needs to be kernel mediated rather than just made a userspace problem)
If this is something custom to keep openeuler happy in the short term them fair enough, but we don't want lots of (OS in the loop) PPR solutions. I did raise an open question in one of those threads on whether anyone currently cares about poison repair on a running system. Interest so far is minor so it maybe challenging to convince people of this except that it might be used on initial boot to remove lines known bad from previous boots etc before onlining the memory.
If we are considering a DAX / ACPI Specific Purpose Memory model for the memory we are considering repairing things get easier as can do repair after tearing down the mappings.
Various comments inline. May well overlap with earlier reviews though!
drivers/soc/hisilicon/Kconfig | 10 + drivers/soc/hisilicon/Makefile | 1 + drivers/soc/hisilicon/hisi_mem_ras.c | 553 +++++++++++++++++++++++++++ drivers/soc/hisilicon/hisi_mem_ras.h | 84 ++++
Needs Documentation/ABI/*
4 files changed, 648 insertions(+) create mode 100644 drivers/soc/hisilicon/hisi_mem_ras.c create mode 100644 drivers/soc/hisilicon/hisi_mem_ras.h
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig index ae8e55a776e0..983cafc38e5c 100644 --- a/drivers/soc/hisilicon/Kconfig +++ b/drivers/soc/hisilicon/Kconfig @@ -44,4 +44,14 @@ config HISI_HBMCACHE To compile the driver as a module, choose M here: the module will be called hisi_hbmcache.
+config HISI_MEM_RAS
- tristate "Add support for HISI Memory repair"
- depends on ACPI
- help
Add Memory repair support for Hisilicon device, which can be used to query
memory repair cap and repair hardware error in memory devices. This feature
need to work with hardware firmwares.
I'd drop the lat sentence. It's a detail the kernel doesn't need.
If not sure say no.
endmenu diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile index 4ef8823250e8..b339a67bcd77 100644 --- a/drivers/soc/hisilicon/Makefile +++ b/drivers/soc/hisilicon/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_KUNPENG_HCCS) += kunpeng_hccs.o obj-$(CONFIG_HISI_HBMDEV) += hisi_hbmdev.o obj-$(CONFIG_HISI_HBMCACHE) += hisi_hbmcache.o obj-$(CONFIG_ARM64_PBHA) += pbha.o +obj-$(CONFIG_HISI_MEM_RAS) += hisi_mem_ras.o diff --git a/drivers/soc/hisilicon/hisi_mem_ras.c b/drivers/soc/hisilicon/hisi_mem_ras.c new file mode 100644 index 000000000000..a29c41ccdba1 --- /dev/null +++ b/drivers/soc/hisilicon/hisi_mem_ras.c @@ -0,0 +1,553 @@ +// SPDX-License-Identifier: GPL-2.0 +/*
- Copyright (C) Huawei Technologies Co., Ltd. 2024. All rights reserved.
- */
+#include <linux/kobject.h> +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/iopoll.h> +#include <linux/platform_device.h> +#include <linux/mm.h> +#include <acpi/pcc.h>
+#include "hisi_mem_ras.h" +#define DRV_NAME "hisi_mem_ras"
+#define HISI_MEM_PCC_CMD_WAIT_RETRIES_NUM 500ULL +#define HISI_MEM_POLL_STATUS_TIME_INTERVAL_US 3
+static struct hisi_mem_ras *mras;
+static int hisi_mem_get_paddr_range(struct hisi_mem_dev *hdev) +{
- struct platform_device *pdev = hdev->pdev;
- struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
- struct hisi_mem_register_ctx ctx = {0};
Might as well set dev here.
- acpi_handle handle = adev->handle;
- acpi_status status;
- if (!acpi_has_method(handle, METHOD_NAME__CRS)) {
dev_err(&pdev->dev, "No _CRS method.\n");
return -ENODEV;
- }
- ctx.dev = &pdev->dev;
- status = acpi_walk_resources(handle, METHOD_NAME__CRS,
hisi_mem_get_paddr_range_cb, &ctx);
- if (ACPI_FAILURE(status))
return ctx.err;
- hdev->paddr_min = ctx.paddr_min;
- hdev->paddr_max = ctx.paddr_max;
- return 0;
+}
+static int hisi_mem_register_pcc_channel(struct hisi_mem_dev *hdev) +{
- struct hisi_mem_mbox_client_info *cl_info = &hdev->cl_info;
- struct mbox_client *cl = &cl_info->client;
- struct pcc_mbox_chan *pcc_chan;
- struct platform_device *pdev = hdev->pdev;
- int rc;
- cl->dev = &pdev->dev;
- cl->tx_block = false;
- cl->knows_txdone = true;
- cl->tx_done = hisi_mem_chan_tx_done;
- cl->rx_callback = hdev->verspec_data->rx_callback;
- init_completion(&cl_info->done);
- pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id);
- if (IS_ERR(pcc_chan)) {
dev_err(&pdev->dev, "PPC channel request failed.\n");
rc = -ENODEV;
goto out;
return dev_err_probe(&pdev->dev, -ENODEV,...);
- }
- cl_info->pcc_chan = pcc_chan;
Maybe worth some __free() magic and delaying setting this until all error paths are done then using a no_free_ptr() to steal the pointer from the autocleanup. Will let you do direct returns and use dev_err_probe() giving cleaner result.
- cl_info->mbox_chan = pcc_chan->mchan;
- /*
* pcc_chan->latency is just a nominal value. In reality the remote
* processor could be much slower to reply. So add an arbitrary amount
* of wait on top of nominal.
*/
- cl_info->deadline_us =
HISI_MEM_PCC_CMD_WAIT_RETRIES_NUM * pcc_chan->latency;
- if (!hdev->verspec_data->has_txdone_irq &&
cl_info->mbox_chan->mbox->txdone_irq) {
dev_err(&pdev->dev, "PCC IRQ in PCCT is enabled.\n");
rc = -EINVAL;
goto err_mbx_channel_free;
- } else if (hdev->verspec_data->has_txdone_irq &&
!cl_info->mbox_chan->mbox->txdone_irq) {
dev_err(&pdev->dev, "PCC IRQ in PCCT isn't supported.\n");
rc = -EINVAL;
goto err_mbx_channel_free;
- }
- if (pcc_chan->shmem_base_addr) {
cl_info->pcc_comm_addr = ioremap(pcc_chan->shmem_base_addr,
pcc_chan->shmem_size);
if (!cl_info->pcc_comm_addr) {
dev_err(&pdev->dev, "Failed to ioremap PCC communication region for channel-%u.\n",
hdev->chan_id);
rc = -ENOMEM;
goto err_mbx_channel_free;
}
- }
- return 0;
+err_mbx_channel_free:
- pcc_mbox_free_channel(cl_info->pcc_chan);
+out:
Never have an out label that just returns. Just return inline as it is easier to read.
- return rc;
+}
+static int hisi_mem_wait_cmd_complete_by_poll(struct hisi_mem_dev *hdev) +{
- struct hisi_mem_mbox_client_info *cl_info = &hdev->cl_info;
- struct acpi_pcct_shared_memory __iomem *comm_base =
cl_info->pcc_comm_addr;
That's an ugly formatting. Just one tab more than line above before cl_info->...
- struct platform_device *pdev = hdev->pdev;
- u16 status;
- int ret;
- /*
* Poll PCC status register every 3us(delay_us) for maximum of
* deadline_us(timeout_us) until PCC command complete bit is set(cond)
*/
- ret = readw_poll_timeout(&comm_base->status, status,
status & PCC_STATUS_CMD_COMPLETE,
HISI_MEM_POLL_STATUS_TIME_INTERVAL_US,
cl_info->deadline_us);
- if (unlikely(ret))
dev_err(&pdev->dev, "poll PCC status failed, ret = %d.\n", ret);
- return ret;
+}
+static int hisi_mem_wait_cmd_complete_by_irq(struct hisi_mem_dev *hdev) +{
- struct hisi_mem_mbox_client_info *cl_info = &hdev->cl_info;
- struct platform_device *pdev = hdev->pdev;
- if (!wait_for_completion_timeout(&cl_info->done,
usecs_to_jiffies(cl_info->deadline_us))) {
dev_err(&pdev->dev, "PCC command executed timeout!\n");
return -ETIMEDOUT;
- }
- return 0;
+}
+static inline void hisi_mem_fill_pcc_shared_mem_region(struct hisi_mem_dev *hdev,
u8 cmd,
struct hisi_mem_desc *desc,
Given this is the request, why use the union rather than struct hisi_mem_req_desc?
void __iomem *comm_space,
u16 space_size)
+{
- struct acpi_pcct_shared_memory tmp = {
.signature = PCC_SIGNATURE | hdev->chan_id,
.command = cmd,
.status = 0,
I'd not bother intializing status. C will do it for you and it's a fairly sensible default.
- };
- memcpy_toio(hdev->cl_info.pcc_comm_addr, (void *)&tmp,
Should never need to cast to or from an void *
sizeof(struct acpi_pcct_shared_memory));
sizeof (*tmp)
- /* Copy the message to the PCC comm space */
- memcpy_toio(comm_space, (void *)desc, space_size);
If desc is less than the size of the memspace this is going to copy in extra. Maybe that's prevented somewhere but it's not obvious to me.
+}
+static inline void hisi_mem_fill_ext_pcc_shared_mem_region(struct hisi_mem_dev *hdev,
u8 cmd,
struct hisi_mem_desc *desc,
void __iomem *comm_space,
u16 space_size)
+{
- struct acpi_pcct_ext_pcc_shared_memory tmp = {
.signature = PCC_SIGNATURE | hdev->chan_id,
.flags = PCC_CMD_COMPLETION_NOTIFY,
.length = HISI_MEM_PCC_SHARE_MEM_BYTES,
.command = cmd,
- };
- memcpy_toio(hdev->cl_info.pcc_comm_addr, (void *)&tmp,
Should be no point in void * casts.
sizeof(struct acpi_pcct_ext_pcc_shared_memory));
sizeof (*tmp)
- /* Copy the message to the PCC comm space */
- memcpy_toio(comm_space, (void *)desc, space_size);
+}
+static const struct hisi_mem_verspecific_data hisi04b1_verspec_data = {
- .rx_callback = NULL,
- .wait_cmd_complete = hisi_mem_wait_cmd_complete_by_poll,
- .fill_pcc_shared_mem = hisi_mem_fill_pcc_shared_mem_region,
- .shared_mem_size = sizeof(struct acpi_pcct_shared_memory),
- .has_txdone_irq = false,
+};
+static const struct hisi_mem_verspecific_data hisi04b2_verspec_data = {
- .rx_callback = hisi_mem_pcc_rx_callback,
- .wait_cmd_complete = hisi_mem_wait_cmd_complete_by_irq,
- .fill_pcc_shared_mem = hisi_mem_fill_ext_pcc_shared_mem_region,
- .shared_mem_size = sizeof(struct acpi_pcct_ext_pcc_shared_memory),
- .has_txdone_irq = true,
+};
+static int hisi_mem_pcc_cmd_send(struct hisi_mem_dev *hdev, u8 cmd,
struct hisi_mem_desc *desc)
The operation isn't done in place, so I'd split this into request and response. They can use same data if that actually makes sense. I'm not sure it does though.
+{
- const struct hisi_mem_verspecific_data *verspec_data = hdev->verspec_data;
- struct hisi_mem_mbox_client_info *cl_info = &hdev->cl_info;
- struct platform_device *pdev = hdev->pdev;
- void __iomem *comm_space;
- u16 space_size;
- int ret;
- comm_space = cl_info->pcc_comm_addr + verspec_data->shared_mem_size;
- space_size = HISI_MEM_PCC_SHARE_MEM_BYTES - verspec_data->shared_mem_size;
- verspec_data->fill_pcc_shared_mem(hdev, cmd, desc,
comm_space, space_size);
- if (verspec_data->has_txdone_irq)
reinit_completion(&cl_info->done);
- /* Ring doorbell */
- ret = mbox_send_message(cl_info->mbox_chan, &cmd);
- if (ret < 0) {
dev_err(&pdev->dev, "Send PCC mbox message failed, ret = %d.\n",
ret);
goto end;
- }
- ret = verspec_data->wait_cmd_complete(hdev);
- if (ret)
goto end;
- /* Copy response data */
- memcpy_fromio((void *)desc, comm_space, space_size);
Should be no need to cast to void *
+end:
- if (verspec_data->has_txdone_irq)
mbox_chan_txdone(cl_info->mbox_chan, ret);
- else
mbox_client_txdone(cl_info->mbox_chan, ret);
- return ret;
+}
+int hisi_mem_do_acls_query(struct hisi_mem_dev *hdev, int *status, u64 paddr) +{
- struct platform_device *pdev = hdev->pdev;
- struct hisi_mem_desc desc;
- int ret;
- memset(&desc, 0, sizeof(desc));
- desc.req.paddr = paddr;
- desc.req.subcmd = HISI_MEM_RAS_QUERY_CAP;
- ret = hisi_mem_pcc_cmd_send(hdev, HISI_MEM_RAS_ACLS, &desc);
Similar comments to below.
- if (ret) {
dev_err(&pdev->dev, "do acls query failed, ret = %d.\n", ret);
return ret;
- }
- *status = desc.rsp.retStatus;
- return 0;
+}
+static ssize_t acls_query_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
+{
- struct hisi_mem_dev *hdev, *tmp;
- int status, ret;
- u64 paddr;
- if (kstrtoull(buf, 16, &paddr))
return -EINVAL;
- list_for_each_entry_safe(hdev, tmp, &mras->list, list) {
struct platform_device *pdev = hdev->pdev;
if (paddr < hdev->paddr_min || paddr > hdev->paddr_max)
continue;
ret = hisi_mem_do_acls_query(hdev, &status, paddr);
if (ret)
return ret;
if (status == HISI_MEM_ACLS_OK) {
return count;
As below, I'd rather see errors out of line and good path inline.
if (status == HISI_MEM_ACLS_QUERY_NO_RES) { } else if (status != HSI_MEM_ACLS_OK) }
return count;
It's a minor thing but if people are reviewing a lot of code this convention saves them thinking about what is good path and what is bad.
} else if (status == HISI_MEM_ACLS_QUERY_NO_RES) {
dev_info(&pdev->dev, "acls query no resource left.\n");
return -ENOSPC;
}
dev_err(&pdev->dev, "acls query error code %d.\n", status);
return -EIO;
- }
- return -ENODEV;
+}
+static struct kobj_attribute acls_query_store_attribute =
- __ATTR(acls_query, 0200, NULL, acls_query_store);
+int hisi_mem_do_acls_repair(struct hisi_mem_dev *hdev, int *status, u64 paddr) +{
- struct hisi_mem_desc desc;
- struct platform_device *pdev;
- int ret;
- memset(&desc, 0, sizeof(desc));
- desc.req.paddr = paddr;
- desc.req.subcmd = HISI_MEM_RAS_DO_REPAIR;
struct hisi_mem_desc desc = { .req = { .paddr = paddr, .subcmd = HISI_MEM_RAS_DO_REPAIR, }, }; Probably better than meset unless the descriptor has holes that we are relying on filling with zero. If so add a comment.
- ret = hisi_mem_pcc_cmd_send(hdev, HISI_MEM_RAS_ACLS, &desc);
As below, I'd use separate storage for req and rsp so we can type them all the way through. They are tiny so who cares about overhead ;)
- if (ret) {
dev_err(&pdev->dev, "do acls repair failed, ret = %d.\n", ret);
return ret;
- }
- *status = desc.rsp.retStatus;
- return 0;
+}
+static ssize_t acls_repair_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
+{
- struct hisi_mem_dev *hdev, *tmp;
- u64 paddr, ret;
- int status;
- if (kstrtoull(buf, 16, &paddr))
return -EINVAL;
- list_for_each_entry_safe(hdev, tmp, &mras->list, list) {
struct platform_device *pdev = hdev->pdev;
if (paddr < hdev->paddr_min || paddr > hdev->paddr_max)
continue;
Hmm. So this is why the unified control.
I suspect the question that an upstream proposal would ask is why can't your instances all have their own controls + padd_min/max exposed in sysfs then issue repair on the right ones only.
A generic interfaces is going to include info on granularity as well. How big is the repair?
ret = hisi_mem_do_acls_repair(hdev, &status, paddr);
if (ret)
return ret;
if (status == HISI_MEM_ACLS_OK)
return count;
Flip this so error condition out of line not the good one.
dev_err(&pdev->dev, "acls repair error code %d.\n", status);
return -EIO;
- }
- return -ENODEV;
+} +static struct kobj_attribute acls_repair_store_attribute =
- __ATTR(acls_repair, 0200, NULL, acls_repair_store);
+static struct attribute *acls_attrs[] = {
- &acls_query_store_attribute.attr,
- &acls_repair_store_attribute.attr,
- NULL,
No comma after null terminators.
+};
+static struct attribute_group acls_attr_group = {
- .attrs = acls_attrs,
+};
AS these are associated with init / exit not probe / remove, move them down to make that association more obvious
+static int hisi_mem_ras_probe(struct platform_device *pdev) +{
- struct hisi_mem_dev *hdev;
- int ret = -ENOMEM;
- hdev = kzalloc(sizeof(struct hisi_mem_dev), GFP_KERNEL);
- if (!hdev)
return -ENOMEM;
- hdev->pdev = pdev;
- ret = hisi_mem_get_pcc_chan_id(hdev);
- if (ret)
goto free_hdev;
- ret = hisi_mem_register_pcc_channel(hdev);
This lot all needs tearing down in remove..
- if (ret)
goto free_hdev;
- platform_set_drvdata(pdev, hdev);
- list_add_tail(&hdev->list, &mras->list);
- hdev->verspec_data = &hisi04b2_verspec_data;
- return 0;
+free_hdev:
- kfree(hdev);
- return ret;
+}
+static int hisi_mem_ras_remove(struct platform_device *pdev) +{
- struct hisi_mem_dev *hdev = platform_get_drvdata(pdev);
- platform_set_drvdata(pdev, NULL);
Should be no need to do that.
- list_del(&hdev->list);
Use a devm_add_action_or_reset() for this.
- return 0;
+}
+static const struct acpi_device_id hisi_mem_acpi_match[] = {
- { "HISI0FF1", 0},
- { },
No need for comma on null terminator.
+}; +MODULE_DEVICE_TABLE(acpi, hisi_mem_acpi_match);
+static struct platform_driver hisi_mem_ras_driver = {
- .probe = hisi_mem_ras_probe,
- .remove = hisi_mem_ras_remove,
- .driver = {
.name = "hisi_mem_ras",
.acpi_match_table = hisi_mem_acpi_match,
- },
+};
+static int __init hisi_mem_ras_init(void) +{
- int ret;
- mras = kzalloc(sizeof(struct hisi_mem_ras), GFP_KERNEL);
sizeof(*mras)
- if (!mras)
return -ENOMEM;
- mras->acls_kobj = kobject_create_and_add("acls", kernel_kobj);
- if (!mras->acls_kobj) {
kfree(mras);
return -ENOMEM;
- }
- ret = sysfs_create_group(mras->acls_kobj, &acls_attr_group);
- if (ret) {
mras->acls_kobj = NULL;
kobject_put(mras->acls_kobj);
Put something you just made NULL? Use a goto for this stuff so it's consistent.
return ret;
- }
This is unusual to see. I guess ABI docs will make it clear what the intent is. Gut feeling is it won't be upstreamable though.
If the aim is bringing the various PPR control instances together then that's what the RAS feature control proposal is all about.
- ret = platform_driver_register(&hisi_mem_ras_driver);
- if (ret) {
sysfs_remove_group(mras->acls_kobj, &acls_attr_group);
kobject_put(mras->acls_kobj);
kfree(mras);
return ret;
- }
- INIT_LIST_HEAD(&mras->list);
- return 0;
+} +module_init(hisi_mem_ras_init);
+static void __exit hisi_mem_ras_exit(void) +{
- platform_driver_unregister(&hisi_mem_ras_driver);
- sysfs_remove_group(mras->acls_kobj, &acls_attr_group);
- kobject_put(mras->acls_kobj);
- kfree(mras);
+} +module_exit(hisi_mem_ras_exit);
+MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Xiaofei Tan tanxiaofei@huawei.com"); +MODULE_DESCRIPTION("HISILICON Memory RAS driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/soc/hisilicon/hisi_mem_ras.h b/drivers/soc/hisilicon/hisi_mem_ras.h new file mode 100644 index 000000000000..a9acbb0c1012 --- /dev/null +++ b/drivers/soc/hisilicon/hisi_mem_ras.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/*
- Copyright (C) Huawei Technologies Co., Ltd. 2023. All rights reserved.
If posting somewhere, update the year.
- */
+#ifndef _HISI_MEM_RAS_H +#define _HISI_MEM_RAS_H
+struct hisi_mem_ras {
- struct list_head list;
- struct kobject *acls_kobj;
+};
+struct hisi_mem_mbox_client_info {
- struct mbox_client client;
- struct mbox_chan *mbox_chan;
- struct pcc_mbox_chan *pcc_chan;
- u64 deadline_us;
- void __iomem *pcc_comm_addr;
- struct completion done;
+};
+struct hisi_mem_desc; +struct hisi_mem_dev;
+struct hisi_mem_verspecific_data {
- void (*rx_callback)(struct mbox_client *cl, void *mssg);
- int (*wait_cmd_complete)(struct hisi_mem_dev *hdev);
- void (*fill_pcc_shared_mem)(struct hisi_mem_dev *hdev,
u8 cmd, struct hisi_mem_desc *desc,
As above, I'm not convinced a fill should take the union. You only fill with requests.
void __iomem *comm_space,
u16 space_size);
- u16 shared_mem_size;
- bool has_txdone_irq;
+};
+struct hisi_mem_dev {
- struct list_head list;
- struct platform_device *pdev;
- //struct acpi_device *acpi_dev;
Tidy that up.
- const struct hisi_mem_verspecific_data *verspec_data;
- /* device capabilities from firmware, like hisi_mem_CAPS_xxx. */
- u64 caps;
- u8 chan_id;
- u64 paddr_min;
- u64 paddr_max;
- struct mutex lock;
- struct hisi_mem_mbox_client_info cl_info;
+};
+#define HISI_MEM_PCC_SHARE_MEM_BYTES 128 +enum hisi_mem_ras_cmd_type {
- HISI_MEM_RAS_ACLS = 0x4,
- HISI_MEM_RAS_PPR = 0X5,
+};
+enum hisi_mem_ras_subcmd_type {
- HISI_MEM_RAS_QUERY_CAP = 0x0,
- HISI_MEM_RAS_DO_REPAIR = 0x1,
+};
+struct hisi_mem_req_desc {
- u64 paddr;
- u8 subcmd;
- u8 rsv[3];
+};
+#define HISI_MEM_ACLS_OK 0 +#define HISI_MEM_ACLS_QUERY_NO_RES 1 +#define HISI_MEM_ACLS_REPAIR_FAIL 2 +#define HISI_MEM_ACLS_EINVAL 3 +struct hisi_mem_rsp_desc {
- u8 retStatus; /* 0: success, other: failure */
- u8 rsv[7];
+};
+struct hisi_mem_desc {
- union {
struct hisi_mem_req_desc req;
struct hisi_mem_rsp_desc rsp;
- };
+};
+#endif