18fdcb170SDan Williams // SPDX-License-Identifier: GPL-2.0-only
28fdcb170SDan Williams /* Copyright(c) 2021 Intel Corporation. All rights reserved. */
38fdcb170SDan Williams #include <linux/libnvdimm.h>
460b8f172SDan Williams #include <asm/unaligned.h>
58fdcb170SDan Williams #include <linux/device.h>
68fdcb170SDan Williams #include <linux/module.h>
721083f51SDan Williams #include <linux/ndctl.h>
821083f51SDan Williams #include <linux/async.h>
98fdcb170SDan Williams #include <linux/slab.h>
1004ad63f0SDan Williams #include <linux/nd.h>
115161a55cSBen Widawsky #include "cxlmem.h"
128fdcb170SDan Williams #include "cxl.h"
138fdcb170SDan Williams
1432828115SDave Jiang extern const struct nvdimm_security_ops *cxl_security_ops;
1532828115SDave Jiang
1612f3856aSDan Williams static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
1712f3856aSDan Williams
clear_exclusive(void * mds)18*59f8d151SDan Williams static void clear_exclusive(void *mds)
1912f3856aSDan Williams {
20*59f8d151SDan Williams clear_exclusive_cxl_commands(mds, exclusive_cmds);
2112f3856aSDan Williams }
2212f3856aSDan Williams
unregister_nvdimm(void * nvdimm)2321083f51SDan Williams static void unregister_nvdimm(void *nvdimm)
2421083f51SDan Williams {
2521083f51SDan Williams nvdimm_delete(nvdimm);
2621083f51SDan Williams }
2721083f51SDan Williams
provider_show(struct device * dev,struct device_attribute * attr,char * buf)28452996faSDave Jiang static ssize_t provider_show(struct device *dev, struct device_attribute *attr, char *buf)
29452996faSDave Jiang {
30452996faSDave Jiang struct nvdimm *nvdimm = to_nvdimm(dev);
31452996faSDave Jiang struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
32452996faSDave Jiang
33452996faSDave Jiang return sysfs_emit(buf, "%s\n", dev_name(&cxl_nvd->dev));
34452996faSDave Jiang }
35452996faSDave Jiang static DEVICE_ATTR_RO(provider);
36452996faSDave Jiang
id_show(struct device * dev,struct device_attribute * attr,char * buf)37bd429e53SDave Jiang static ssize_t id_show(struct device *dev, struct device_attribute *attr, char *buf)
38bd429e53SDave Jiang {
39bd429e53SDave Jiang struct nvdimm *nvdimm = to_nvdimm(dev);
40bd429e53SDave Jiang struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
41bd429e53SDave Jiang struct cxl_dev_state *cxlds = cxl_nvd->cxlmd->cxlds;
42bd429e53SDave Jiang
43bd429e53SDave Jiang return sysfs_emit(buf, "%lld\n", cxlds->serial);
44bd429e53SDave Jiang }
45bd429e53SDave Jiang static DEVICE_ATTR_RO(id);
46bd429e53SDave Jiang
47bd429e53SDave Jiang static struct attribute *cxl_dimm_attributes[] = {
48bd429e53SDave Jiang &dev_attr_id.attr,
49452996faSDave Jiang &dev_attr_provider.attr,
50bd429e53SDave Jiang NULL
51bd429e53SDave Jiang };
52bd429e53SDave Jiang
53bd429e53SDave Jiang static const struct attribute_group cxl_dimm_attribute_group = {
54bd429e53SDave Jiang .name = "cxl",
55bd429e53SDave Jiang .attrs = cxl_dimm_attributes,
56bd429e53SDave Jiang };
57bd429e53SDave Jiang
58bd429e53SDave Jiang static const struct attribute_group *cxl_dimm_attribute_groups[] = {
59bd429e53SDave Jiang &cxl_dimm_attribute_group,
60bd429e53SDave Jiang NULL
61bd429e53SDave Jiang };
62bd429e53SDave Jiang
cxl_nvdimm_probe(struct device * dev)6321083f51SDan Williams static int cxl_nvdimm_probe(struct device *dev)
6421083f51SDan Williams {
6521083f51SDan Williams struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);
6612f3856aSDan Williams struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
67f17b558dSDan Williams struct cxl_nvdimm_bridge *cxl_nvb = cxlmd->cxl_nvb;
68*59f8d151SDan Williams struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
6960b8f172SDan Williams unsigned long flags = 0, cmd_mask = 0;
7021083f51SDan Williams struct nvdimm *nvdimm;
7112f3856aSDan Williams int rc;
7221083f51SDan Williams
73*59f8d151SDan Williams set_exclusive_cxl_commands(mds, exclusive_cmds);
74*59f8d151SDan Williams rc = devm_add_action_or_reset(dev, clear_exclusive, mds);
7512f3856aSDan Williams if (rc)
76f17b558dSDan Williams return rc;
7721083f51SDan Williams
7821083f51SDan Williams set_bit(NDD_LABELING, &flags);
79f57aec44SDan Williams set_bit(NDD_REGISTER_SYNC, &flags);
8060b8f172SDan Williams set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
8160b8f172SDan Williams set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
8260b8f172SDan Williams set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
83bd429e53SDave Jiang nvdimm = __nvdimm_create(cxl_nvb->nvdimm_bus, cxl_nvd,
84bd429e53SDave Jiang cxl_dimm_attribute_groups, flags,
85b5807c80SDave Jiang cmd_mask, 0, NULL, cxl_nvd->dev_id,
86b5807c80SDave Jiang cxl_security_ops, NULL);
87f17b558dSDan Williams if (!nvdimm)
88f17b558dSDan Williams return -ENOMEM;
8921083f51SDan Williams
9012f3856aSDan Williams dev_set_drvdata(dev, nvdimm);
91f17b558dSDan Williams return devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm);
9221083f51SDan Williams }
9321083f51SDan Williams
9421083f51SDan Williams static struct cxl_driver cxl_nvdimm_driver = {
9521083f51SDan Williams .name = "cxl_nvdimm",
9621083f51SDan Williams .probe = cxl_nvdimm_probe,
9721083f51SDan Williams .id = CXL_DEVICE_NVDIMM,
98cb9cfff8SDan Williams .drv = {
99cb9cfff8SDan Williams .suppress_bind_attrs = true,
100cb9cfff8SDan Williams },
10121083f51SDan Williams };
10221083f51SDan Williams
cxl_pmem_get_config_size(struct cxl_memdev_state * mds,struct nd_cmd_get_config_size * cmd,unsigned int buf_len)103*59f8d151SDan Williams static int cxl_pmem_get_config_size(struct cxl_memdev_state *mds,
10460b8f172SDan Williams struct nd_cmd_get_config_size *cmd,
10560b8f172SDan Williams unsigned int buf_len)
10660b8f172SDan Williams {
10760b8f172SDan Williams if (sizeof(*cmd) > buf_len)
10860b8f172SDan Williams return -EINVAL;
10960b8f172SDan Williams
11060b8f172SDan Williams *cmd = (struct nd_cmd_get_config_size){
111*59f8d151SDan Williams .config_size = mds->lsa_size,
112*59f8d151SDan Williams .max_xfer =
113*59f8d151SDan Williams mds->payload_size - sizeof(struct cxl_mbox_set_lsa),
11460b8f172SDan Williams };
11560b8f172SDan Williams
11660b8f172SDan Williams return 0;
11760b8f172SDan Williams }
11860b8f172SDan Williams
cxl_pmem_get_config_data(struct cxl_memdev_state * mds,struct nd_cmd_get_config_data_hdr * cmd,unsigned int buf_len)119*59f8d151SDan Williams static int cxl_pmem_get_config_data(struct cxl_memdev_state *mds,
12060b8f172SDan Williams struct nd_cmd_get_config_data_hdr *cmd,
12160b8f172SDan Williams unsigned int buf_len)
12260b8f172SDan Williams {
12349be6dd8SDan Williams struct cxl_mbox_get_lsa get_lsa;
1245331cdf4SDan Williams struct cxl_mbox_cmd mbox_cmd;
12560b8f172SDan Williams int rc;
12660b8f172SDan Williams
12760b8f172SDan Williams if (sizeof(*cmd) > buf_len)
12860b8f172SDan Williams return -EINVAL;
12960b8f172SDan Williams if (struct_size(cmd, out_buf, cmd->in_length) > buf_len)
13060b8f172SDan Williams return -EINVAL;
13160b8f172SDan Williams
13260b8f172SDan Williams get_lsa = (struct cxl_mbox_get_lsa) {
1338a664875SAlison Schofield .offset = cpu_to_le32(cmd->in_offset),
1348a664875SAlison Schofield .length = cpu_to_le32(cmd->in_length),
13560b8f172SDan Williams };
1365331cdf4SDan Williams mbox_cmd = (struct cxl_mbox_cmd) {
1375331cdf4SDan Williams .opcode = CXL_MBOX_OP_GET_LSA,
1385331cdf4SDan Williams .payload_in = &get_lsa,
1395331cdf4SDan Williams .size_in = sizeof(get_lsa),
1405331cdf4SDan Williams .size_out = cmd->in_length,
1415331cdf4SDan Williams .payload_out = cmd->out_buf,
1425331cdf4SDan Williams };
14360b8f172SDan Williams
144*59f8d151SDan Williams rc = cxl_internal_send_cmd(mds, &mbox_cmd);
14560b8f172SDan Williams cmd->status = 0;
14660b8f172SDan Williams
14760b8f172SDan Williams return rc;
14860b8f172SDan Williams }
14960b8f172SDan Williams
cxl_pmem_set_config_data(struct cxl_memdev_state * mds,struct nd_cmd_set_config_hdr * cmd,unsigned int buf_len)150*59f8d151SDan Williams static int cxl_pmem_set_config_data(struct cxl_memdev_state *mds,
15160b8f172SDan Williams struct nd_cmd_set_config_hdr *cmd,
15260b8f172SDan Williams unsigned int buf_len)
15360b8f172SDan Williams {
15449be6dd8SDan Williams struct cxl_mbox_set_lsa *set_lsa;
1555331cdf4SDan Williams struct cxl_mbox_cmd mbox_cmd;
15660b8f172SDan Williams int rc;
15760b8f172SDan Williams
15860b8f172SDan Williams if (sizeof(*cmd) > buf_len)
15960b8f172SDan Williams return -EINVAL;
16060b8f172SDan Williams
16160b8f172SDan Williams /* 4-byte status follows the input data in the payload */
1624f1aa35fSYu Zhe if (size_add(struct_size(cmd, in_buf, cmd->in_length), 4) > buf_len)
16360b8f172SDan Williams return -EINVAL;
16460b8f172SDan Williams
16560b8f172SDan Williams set_lsa =
16660b8f172SDan Williams kvzalloc(struct_size(set_lsa, data, cmd->in_length), GFP_KERNEL);
16760b8f172SDan Williams if (!set_lsa)
16860b8f172SDan Williams return -ENOMEM;
16960b8f172SDan Williams
17060b8f172SDan Williams *set_lsa = (struct cxl_mbox_set_lsa) {
1718a664875SAlison Schofield .offset = cpu_to_le32(cmd->in_offset),
17260b8f172SDan Williams };
17360b8f172SDan Williams memcpy(set_lsa->data, cmd->in_buf, cmd->in_length);
1745331cdf4SDan Williams mbox_cmd = (struct cxl_mbox_cmd) {
1755331cdf4SDan Williams .opcode = CXL_MBOX_OP_SET_LSA,
1765331cdf4SDan Williams .payload_in = set_lsa,
1775331cdf4SDan Williams .size_in = struct_size(set_lsa, data, cmd->in_length),
1785331cdf4SDan Williams };
17960b8f172SDan Williams
180*59f8d151SDan Williams rc = cxl_internal_send_cmd(mds, &mbox_cmd);
18160b8f172SDan Williams
18260b8f172SDan Williams /*
18360b8f172SDan Williams * Set "firmware" status (4-packed bytes at the end of the input
18460b8f172SDan Williams * payload.
18560b8f172SDan Williams */
18660b8f172SDan Williams put_unaligned(0, (u32 *) &cmd->in_buf[cmd->in_length]);
18760b8f172SDan Williams kvfree(set_lsa);
18860b8f172SDan Williams
18960b8f172SDan Williams return rc;
19060b8f172SDan Williams }
19160b8f172SDan Williams
cxl_pmem_nvdimm_ctl(struct nvdimm * nvdimm,unsigned int cmd,void * buf,unsigned int buf_len)19260b8f172SDan Williams static int cxl_pmem_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
19360b8f172SDan Williams void *buf, unsigned int buf_len)
19460b8f172SDan Williams {
19560b8f172SDan Williams struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
19660b8f172SDan Williams unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
19760b8f172SDan Williams struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
198*59f8d151SDan Williams struct cxl_memdev_state *mds = to_cxl_memdev_state(cxlmd->cxlds);
19960b8f172SDan Williams
20060b8f172SDan Williams if (!test_bit(cmd, &cmd_mask))
20160b8f172SDan Williams return -ENOTTY;
20260b8f172SDan Williams
20360b8f172SDan Williams switch (cmd) {
20460b8f172SDan Williams case ND_CMD_GET_CONFIG_SIZE:
205*59f8d151SDan Williams return cxl_pmem_get_config_size(mds, buf, buf_len);
20660b8f172SDan Williams case ND_CMD_GET_CONFIG_DATA:
207*59f8d151SDan Williams return cxl_pmem_get_config_data(mds, buf, buf_len);
20860b8f172SDan Williams case ND_CMD_SET_CONFIG_DATA:
209*59f8d151SDan Williams return cxl_pmem_set_config_data(mds, buf, buf_len);
21060b8f172SDan Williams default:
21160b8f172SDan Williams return -ENOTTY;
21260b8f172SDan Williams }
21360b8f172SDan Williams }
21460b8f172SDan Williams
cxl_pmem_ctl(struct nvdimm_bus_descriptor * nd_desc,struct nvdimm * nvdimm,unsigned int cmd,void * buf,unsigned int buf_len,int * cmd_rc)2158fdcb170SDan Williams static int cxl_pmem_ctl(struct nvdimm_bus_descriptor *nd_desc,
2168fdcb170SDan Williams struct nvdimm *nvdimm, unsigned int cmd, void *buf,
2178fdcb170SDan Williams unsigned int buf_len, int *cmd_rc)
2188fdcb170SDan Williams {
21960b8f172SDan Williams /*
22060b8f172SDan Williams * No firmware response to translate, let the transport error
22160b8f172SDan Williams * code take precedence.
22260b8f172SDan Williams */
22360b8f172SDan Williams *cmd_rc = 0;
22460b8f172SDan Williams
22560b8f172SDan Williams if (!nvdimm)
2268fdcb170SDan Williams return -ENOTTY;
22760b8f172SDan Williams return cxl_pmem_nvdimm_ctl(nvdimm, cmd, buf, buf_len);
2288fdcb170SDan Williams }
2298fdcb170SDan Williams
detach_nvdimm(struct device * dev,void * data)23019398821SDan Williams static int detach_nvdimm(struct device *dev, void *data)
23119398821SDan Williams {
23219398821SDan Williams struct cxl_nvdimm *cxl_nvd;
23319398821SDan Williams bool release = false;
23419398821SDan Williams
23519398821SDan Williams if (!is_cxl_nvdimm(dev))
23619398821SDan Williams return 0;
23719398821SDan Williams
23819398821SDan Williams device_lock(dev);
23919398821SDan Williams if (!dev->driver)
24019398821SDan Williams goto out;
24119398821SDan Williams
24219398821SDan Williams cxl_nvd = to_cxl_nvdimm(dev);
24319398821SDan Williams if (cxl_nvd->cxlmd && cxl_nvd->cxlmd->cxl_nvb == data)
24419398821SDan Williams release = true;
24519398821SDan Williams out:
24619398821SDan Williams device_unlock(dev);
24719398821SDan Williams if (release)
24819398821SDan Williams device_release_driver(dev);
24919398821SDan Williams return 0;
25019398821SDan Williams }
25119398821SDan Williams
unregister_nvdimm_bus(void * _cxl_nvb)252f17b558dSDan Williams static void unregister_nvdimm_bus(void *_cxl_nvb)
253f17b558dSDan Williams {
254f17b558dSDan Williams struct cxl_nvdimm_bridge *cxl_nvb = _cxl_nvb;
255f17b558dSDan Williams struct nvdimm_bus *nvdimm_bus = cxl_nvb->nvdimm_bus;
256f17b558dSDan Williams
25719398821SDan Williams bus_for_each_dev(&cxl_bus_type, NULL, cxl_nvb, detach_nvdimm);
25819398821SDan Williams
259f17b558dSDan Williams cxl_nvb->nvdimm_bus = NULL;
260f17b558dSDan Williams nvdimm_bus_unregister(nvdimm_bus);
261f17b558dSDan Williams }
262f17b558dSDan Williams
cxl_nvdimm_bridge_probe(struct device * dev)2638fdcb170SDan Williams static int cxl_nvdimm_bridge_probe(struct device *dev)
2648fdcb170SDan Williams {
2658fdcb170SDan Williams struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
2668fdcb170SDan Williams
2678fdcb170SDan Williams cxl_nvb->nd_desc = (struct nvdimm_bus_descriptor) {
2688fdcb170SDan Williams .provider_name = "CXL",
2698fdcb170SDan Williams .module = THIS_MODULE,
2708fdcb170SDan Williams .ndctl = cxl_pmem_ctl,
2718fdcb170SDan Williams };
2728fdcb170SDan Williams
273f17b558dSDan Williams cxl_nvb->nvdimm_bus =
274f17b558dSDan Williams nvdimm_bus_register(&cxl_nvb->dev, &cxl_nvb->nd_desc);
275f17b558dSDan Williams
276f17b558dSDan Williams if (!cxl_nvb->nvdimm_bus)
277f17b558dSDan Williams return -ENOMEM;
278f17b558dSDan Williams
279f17b558dSDan Williams return devm_add_action_or_reset(dev, unregister_nvdimm_bus, cxl_nvb);
2808fdcb170SDan Williams }
2818fdcb170SDan Williams
2828fdcb170SDan Williams static struct cxl_driver cxl_nvdimm_bridge_driver = {
2838fdcb170SDan Williams .name = "cxl_nvdimm_bridge",
2848fdcb170SDan Williams .probe = cxl_nvdimm_bridge_probe,
2858fdcb170SDan Williams .id = CXL_DEVICE_NVDIMM_BRIDGE,
286cb9cfff8SDan Williams .drv = {
287cb9cfff8SDan Williams .suppress_bind_attrs = true,
288cb9cfff8SDan Williams },
2898fdcb170SDan Williams };
2908fdcb170SDan Williams
unregister_nvdimm_region(void * nd_region)29104ad63f0SDan Williams static void unregister_nvdimm_region(void *nd_region)
29204ad63f0SDan Williams {
2934d07ae22SDan Williams nvdimm_region_delete(nd_region);
2944d07ae22SDan Williams }
29504ad63f0SDan Williams
cxlr_pmem_remove_resource(void * res)29604ad63f0SDan Williams static void cxlr_pmem_remove_resource(void *res)
29704ad63f0SDan Williams {
29804ad63f0SDan Williams remove_resource(res);
29904ad63f0SDan Williams }
30004ad63f0SDan Williams
30104ad63f0SDan Williams struct cxl_pmem_region_info {
30204ad63f0SDan Williams u64 offset;
30304ad63f0SDan Williams u64 serial;
30404ad63f0SDan Williams };
30504ad63f0SDan Williams
cxl_pmem_region_probe(struct device * dev)30604ad63f0SDan Williams static int cxl_pmem_region_probe(struct device *dev)
30704ad63f0SDan Williams {
30804ad63f0SDan Williams struct nd_mapping_desc mappings[CXL_DECODER_MAX_INTERLEAVE];
30904ad63f0SDan Williams struct cxl_pmem_region *cxlr_pmem = to_cxl_pmem_region(dev);
31004ad63f0SDan Williams struct cxl_region *cxlr = cxlr_pmem->cxlr;
311f17b558dSDan Williams struct cxl_nvdimm_bridge *cxl_nvb = cxlr->cxl_nvb;
31204ad63f0SDan Williams struct cxl_pmem_region_info *info = NULL;
31304ad63f0SDan Williams struct nd_interleave_set *nd_set;
31404ad63f0SDan Williams struct nd_region_desc ndr_desc;
31504ad63f0SDan Williams struct cxl_nvdimm *cxl_nvd;
31604ad63f0SDan Williams struct nvdimm *nvdimm;
31704ad63f0SDan Williams struct resource *res;
31804ad63f0SDan Williams int rc, i = 0;
31904ad63f0SDan Williams
32004ad63f0SDan Williams memset(&mappings, 0, sizeof(mappings));
32104ad63f0SDan Williams memset(&ndr_desc, 0, sizeof(ndr_desc));
32204ad63f0SDan Williams
32304ad63f0SDan Williams res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL);
324f17b558dSDan Williams if (!res)
325f17b558dSDan Williams return -ENOMEM;
32604ad63f0SDan Williams
32704ad63f0SDan Williams res->name = "Persistent Memory";
32804ad63f0SDan Williams res->start = cxlr_pmem->hpa_range.start;
32904ad63f0SDan Williams res->end = cxlr_pmem->hpa_range.end;
33004ad63f0SDan Williams res->flags = IORESOURCE_MEM;
33104ad63f0SDan Williams res->desc = IORES_DESC_PERSISTENT_MEMORY;
33204ad63f0SDan Williams
33304ad63f0SDan Williams rc = insert_resource(&iomem_resource, res);
33404ad63f0SDan Williams if (rc)
335f17b558dSDan Williams return rc;
33604ad63f0SDan Williams
33704ad63f0SDan Williams rc = devm_add_action_or_reset(dev, cxlr_pmem_remove_resource, res);
33804ad63f0SDan Williams if (rc)
339f17b558dSDan Williams return rc;
34004ad63f0SDan Williams
34104ad63f0SDan Williams ndr_desc.res = res;
34204ad63f0SDan Williams ndr_desc.provider_data = cxlr_pmem;
34304ad63f0SDan Williams
34404ad63f0SDan Williams ndr_desc.numa_node = memory_add_physaddr_to_nid(res->start);
34504ad63f0SDan Williams ndr_desc.target_node = phys_to_target_node(res->start);
34604ad63f0SDan Williams if (ndr_desc.target_node == NUMA_NO_NODE) {
34704ad63f0SDan Williams ndr_desc.target_node = ndr_desc.numa_node;
34804ad63f0SDan Williams dev_dbg(&cxlr->dev, "changing target node from %d to %d",
34904ad63f0SDan Williams NUMA_NO_NODE, ndr_desc.target_node);
35004ad63f0SDan Williams }
35104ad63f0SDan Williams
35204ad63f0SDan Williams nd_set = devm_kzalloc(dev, sizeof(*nd_set), GFP_KERNEL);
353f17b558dSDan Williams if (!nd_set)
354f17b558dSDan Williams return -ENOMEM;
35504ad63f0SDan Williams
35604ad63f0SDan Williams ndr_desc.memregion = cxlr->id;
35704ad63f0SDan Williams set_bit(ND_REGION_CXL, &ndr_desc.flags);
35804ad63f0SDan Williams set_bit(ND_REGION_PERSIST_MEMCTRL, &ndr_desc.flags);
35904ad63f0SDan Williams
36004ad63f0SDan Williams info = kmalloc_array(cxlr_pmem->nr_mappings, sizeof(*info), GFP_KERNEL);
361f17b558dSDan Williams if (!info)
362f17b558dSDan Williams return -ENOMEM;
36304ad63f0SDan Williams
36404ad63f0SDan Williams for (i = 0; i < cxlr_pmem->nr_mappings; i++) {
36504ad63f0SDan Williams struct cxl_pmem_region_mapping *m = &cxlr_pmem->mapping[i];
36604ad63f0SDan Williams struct cxl_memdev *cxlmd = m->cxlmd;
36704ad63f0SDan Williams struct cxl_dev_state *cxlds = cxlmd->cxlds;
36804ad63f0SDan Williams
369f17b558dSDan Williams cxl_nvd = cxlmd->cxl_nvd;
37004ad63f0SDan Williams nvdimm = dev_get_drvdata(&cxl_nvd->dev);
37104ad63f0SDan Williams if (!nvdimm) {
37204ad63f0SDan Williams dev_dbg(dev, "[%d]: %s: no nvdimm found\n", i,
37304ad63f0SDan Williams dev_name(&cxlmd->dev));
37404ad63f0SDan Williams rc = -ENODEV;
3754d07ae22SDan Williams goto out_nvd;
37604ad63f0SDan Williams }
3774d07ae22SDan Williams
37804ad63f0SDan Williams m->cxl_nvd = cxl_nvd;
37904ad63f0SDan Williams mappings[i] = (struct nd_mapping_desc) {
38004ad63f0SDan Williams .nvdimm = nvdimm,
38104ad63f0SDan Williams .start = m->start,
38204ad63f0SDan Williams .size = m->size,
38304ad63f0SDan Williams .position = i,
38404ad63f0SDan Williams };
38504ad63f0SDan Williams info[i].offset = m->start;
38604ad63f0SDan Williams info[i].serial = cxlds->serial;
38704ad63f0SDan Williams }
38804ad63f0SDan Williams ndr_desc.num_mappings = cxlr_pmem->nr_mappings;
38904ad63f0SDan Williams ndr_desc.mapping = mappings;
39004ad63f0SDan Williams
39104ad63f0SDan Williams /*
39204ad63f0SDan Williams * TODO enable CXL labels which skip the need for 'interleave-set cookie'
39304ad63f0SDan Williams */
39404ad63f0SDan Williams nd_set->cookie1 =
39504ad63f0SDan Williams nd_fletcher64(info, sizeof(*info) * cxlr_pmem->nr_mappings, 0);
39604ad63f0SDan Williams nd_set->cookie2 = nd_set->cookie1;
39704ad63f0SDan Williams ndr_desc.nd_set = nd_set;
39804ad63f0SDan Williams
39904ad63f0SDan Williams cxlr_pmem->nd_region =
40004ad63f0SDan Williams nvdimm_pmem_region_create(cxl_nvb->nvdimm_bus, &ndr_desc);
4019fd2cf4dSDan Carpenter if (!cxlr_pmem->nd_region) {
4029fd2cf4dSDan Carpenter rc = -ENOMEM;
4034d07ae22SDan Williams goto out_nvd;
40404ad63f0SDan Williams }
40504ad63f0SDan Williams
40604ad63f0SDan Williams rc = devm_add_action_or_reset(dev, unregister_nvdimm_region,
40704ad63f0SDan Williams cxlr_pmem->nd_region);
4084d07ae22SDan Williams out_nvd:
40904ad63f0SDan Williams kfree(info);
41004ad63f0SDan Williams
41104ad63f0SDan Williams return rc;
41204ad63f0SDan Williams }
41304ad63f0SDan Williams
41404ad63f0SDan Williams static struct cxl_driver cxl_pmem_region_driver = {
41504ad63f0SDan Williams .name = "cxl_pmem_region",
41604ad63f0SDan Williams .probe = cxl_pmem_region_probe,
41704ad63f0SDan Williams .id = CXL_DEVICE_PMEM_REGION,
418cb9cfff8SDan Williams .drv = {
419cb9cfff8SDan Williams .suppress_bind_attrs = true,
420cb9cfff8SDan Williams },
42104ad63f0SDan Williams };
42204ad63f0SDan Williams
cxl_pmem_init(void)4238fdcb170SDan Williams static __init int cxl_pmem_init(void)
4248fdcb170SDan Williams {
4258fdcb170SDan Williams int rc;
4268fdcb170SDan Williams
42712f3856aSDan Williams set_bit(CXL_MEM_COMMAND_ID_SET_SHUTDOWN_STATE, exclusive_cmds);
42812f3856aSDan Williams set_bit(CXL_MEM_COMMAND_ID_SET_LSA, exclusive_cmds);
42912f3856aSDan Williams
4308fdcb170SDan Williams rc = cxl_driver_register(&cxl_nvdimm_bridge_driver);
4318fdcb170SDan Williams if (rc)
43203ff079aSDan Williams return rc;
43321083f51SDan Williams
43421083f51SDan Williams rc = cxl_driver_register(&cxl_nvdimm_driver);
43521083f51SDan Williams if (rc)
43621083f51SDan Williams goto err_nvdimm;
4378fdcb170SDan Williams
43804ad63f0SDan Williams rc = cxl_driver_register(&cxl_pmem_region_driver);
43904ad63f0SDan Williams if (rc)
44004ad63f0SDan Williams goto err_region;
44104ad63f0SDan Williams
4428fdcb170SDan Williams return 0;
4438fdcb170SDan Williams
44404ad63f0SDan Williams err_region:
44504ad63f0SDan Williams cxl_driver_unregister(&cxl_nvdimm_driver);
44621083f51SDan Williams err_nvdimm:
44721083f51SDan Williams cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
4488fdcb170SDan Williams return rc;
4498fdcb170SDan Williams }
4508fdcb170SDan Williams
cxl_pmem_exit(void)4518fdcb170SDan Williams static __exit void cxl_pmem_exit(void)
4528fdcb170SDan Williams {
45304ad63f0SDan Williams cxl_driver_unregister(&cxl_pmem_region_driver);
45421083f51SDan Williams cxl_driver_unregister(&cxl_nvdimm_driver);
4558fdcb170SDan Williams cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
4568fdcb170SDan Williams }
4578fdcb170SDan Williams
4588fdcb170SDan Williams MODULE_LICENSE("GPL v2");
4598fdcb170SDan Williams module_init(cxl_pmem_init);
4608fdcb170SDan Williams module_exit(cxl_pmem_exit);
4618fdcb170SDan Williams MODULE_IMPORT_NS(CXL);
4628fdcb170SDan Williams MODULE_ALIAS_CXL(CXL_DEVICE_NVDIMM_BRIDGE);
46321083f51SDan Williams MODULE_ALIAS_CXL(CXL_DEVICE_NVDIMM);
46404ad63f0SDan Williams MODULE_ALIAS_CXL(CXL_DEVICE_PMEM_REGION);
465