cxl/pci: Fixup devm_cxl_iomap_block() to take a 'struct device *'
[linux-2.6-microblaze.git] / drivers / cxl / core.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright(c) 2020 Intel Corporation. All rights reserved. */
3 #include <linux/io-64-nonatomic-lo-hi.h>
4 #include <linux/device.h>
5 #include <linux/module.h>
6 #include <linux/pci.h>
7 #include "cxl.h"
8
9 /**
10  * DOC: cxl core
11  *
12  * The CXL core provides a sysfs hierarchy for control devices and a rendezvous
13  * point for cross-device interleave coordination through cxl ports.
14  */
15
16 /**
17  * cxl_probe_component_regs() - Detect CXL Component register blocks
18  * @dev: Host device of the @base mapping
19  * @base: Mapping containing the HDM Decoder Capability Header
20  * @map: Map object describing the register block information found
21  *
22  * See CXL 2.0 8.2.4 Component Register Layout and Definition
23  * See CXL 2.0 8.2.5.5 CXL Device Register Interface
24  *
25  * Probe for component register information and return it in map object.
26  */
27 void cxl_probe_component_regs(struct device *dev, void __iomem *base,
28                               struct cxl_component_reg_map *map)
29 {
30         int cap, cap_count;
31         u64 cap_array;
32
33         *map = (struct cxl_component_reg_map) { 0 };
34
35         /*
36          * CXL.cache and CXL.mem registers are at offset 0x1000 as defined in
37          * CXL 2.0 8.2.4 Table 141.
38          */
39         base += CXL_CM_OFFSET;
40
41         cap_array = readq(base + CXL_CM_CAP_HDR_OFFSET);
42
43         if (FIELD_GET(CXL_CM_CAP_HDR_ID_MASK, cap_array) != CM_CAP_HDR_CAP_ID) {
44                 dev_err(dev,
45                         "Couldn't locate the CXL.cache and CXL.mem capability array header./n");
46                 return;
47         }
48
49         /* It's assumed that future versions will be backward compatible */
50         cap_count = FIELD_GET(CXL_CM_CAP_HDR_ARRAY_SIZE_MASK, cap_array);
51
52         for (cap = 1; cap <= cap_count; cap++) {
53                 void __iomem *register_block;
54                 u32 hdr;
55                 int decoder_cnt;
56                 u16 cap_id, offset;
57                 u32 length;
58
59                 hdr = readl(base + cap * 0x4);
60
61                 cap_id = FIELD_GET(CXL_CM_CAP_HDR_ID_MASK, hdr);
62                 offset = FIELD_GET(CXL_CM_CAP_PTR_MASK, hdr);
63                 register_block = base + offset;
64
65                 switch (cap_id) {
66                 case CXL_CM_CAP_CAP_ID_HDM:
67                         dev_dbg(dev, "found HDM decoder capability (0x%x)\n",
68                                 offset);
69
70                         hdr = readl(register_block);
71
72                         decoder_cnt = FIELD_GET(CXL_HDM_DECODER_COUNT_MASK, hdr);
73                         length = 0x20 * decoder_cnt + 0x10;
74
75                         map->hdm_decoder.valid = true;
76                         map->hdm_decoder.offset = offset;
77                         map->hdm_decoder.size = length;
78                         break;
79                 default:
80                         dev_dbg(dev, "Unknown CM cap ID: %d (0x%x)\n", cap_id,
81                                 offset);
82                         break;
83                 }
84         }
85 }
86 EXPORT_SYMBOL_GPL(cxl_probe_component_regs);
87
88 /**
89  * cxl_probe_device_regs() - Detect CXL Device register blocks
90  * @dev: Host device of the @base mapping
91  * @base: Mapping of CXL 2.0 8.2.8 CXL Device Register Interface
92  * @map: Map object describing the register block information found
93  *
94  * Probe for device register information and return it in map object.
95  */
96 void cxl_probe_device_regs(struct device *dev, void __iomem *base,
97                            struct cxl_device_reg_map *map)
98 {
99         int cap, cap_count;
100         u64 cap_array;
101
102         *map = (struct cxl_device_reg_map){ 0 };
103
104         cap_array = readq(base + CXLDEV_CAP_ARRAY_OFFSET);
105         if (FIELD_GET(CXLDEV_CAP_ARRAY_ID_MASK, cap_array) !=
106             CXLDEV_CAP_ARRAY_CAP_ID)
107                 return;
108
109         cap_count = FIELD_GET(CXLDEV_CAP_ARRAY_COUNT_MASK, cap_array);
110
111         for (cap = 1; cap <= cap_count; cap++) {
112                 u32 offset, length;
113                 u16 cap_id;
114
115                 cap_id = FIELD_GET(CXLDEV_CAP_HDR_CAP_ID_MASK,
116                                    readl(base + cap * 0x10));
117                 offset = readl(base + cap * 0x10 + 0x4);
118                 length = readl(base + cap * 0x10 + 0x8);
119
120                 switch (cap_id) {
121                 case CXLDEV_CAP_CAP_ID_DEVICE_STATUS:
122                         dev_dbg(dev, "found Status capability (0x%x)\n", offset);
123
124                         map->status.valid = true;
125                         map->status.offset = offset;
126                         map->status.size = length;
127                         break;
128                 case CXLDEV_CAP_CAP_ID_PRIMARY_MAILBOX:
129                         dev_dbg(dev, "found Mailbox capability (0x%x)\n", offset);
130                         map->mbox.valid = true;
131                         map->mbox.offset = offset;
132                         map->mbox.size = length;
133                         break;
134                 case CXLDEV_CAP_CAP_ID_SECONDARY_MAILBOX:
135                         dev_dbg(dev, "found Secondary Mailbox capability (0x%x)\n", offset);
136                         break;
137                 case CXLDEV_CAP_CAP_ID_MEMDEV:
138                         dev_dbg(dev, "found Memory Device capability (0x%x)\n", offset);
139                         map->memdev.valid = true;
140                         map->memdev.offset = offset;
141                         map->memdev.size = length;
142                         break;
143                 default:
144                         if (cap_id >= 0x8000)
145                                 dev_dbg(dev, "Vendor cap ID: %#x offset: %#x\n", cap_id, offset);
146                         else
147                                 dev_dbg(dev, "Unknown cap ID: %#x offset: %#x\n", cap_id, offset);
148                         break;
149                 }
150         }
151 }
152 EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
153
154 static void __iomem *devm_cxl_iomap_block(struct device *dev,
155                                           resource_size_t addr,
156                                           resource_size_t length)
157 {
158         void __iomem *ret_val;
159         struct resource *res;
160
161         res = devm_request_mem_region(dev, addr, length, dev_name(dev));
162         if (!res) {
163                 resource_size_t end = addr + length - 1;
164
165                 dev_err(dev, "Failed to request region %pa-%pa\n", &addr, &end);
166                 return NULL;
167         }
168
169         ret_val = devm_ioremap(dev, addr, length);
170         if (!ret_val)
171                 dev_err(dev, "Failed to map region %pr\n", res);
172
173         return ret_val;
174 }
175
176 int cxl_map_component_regs(struct pci_dev *pdev,
177                            struct cxl_component_regs *regs,
178                            struct cxl_register_map *map)
179 {
180         struct device *dev = &pdev->dev;
181         resource_size_t phys_addr;
182         resource_size_t length;
183
184         phys_addr = pci_resource_start(pdev, map->barno);
185         phys_addr += map->block_offset;
186
187         phys_addr += map->component_map.hdm_decoder.offset;
188         length = map->component_map.hdm_decoder.size;
189         regs->hdm_decoder = devm_cxl_iomap_block(dev, phys_addr, length);
190         if (!regs->hdm_decoder)
191                 return -ENOMEM;
192
193         return 0;
194 }
195 EXPORT_SYMBOL_GPL(cxl_map_component_regs);
196
197 int cxl_map_device_regs(struct pci_dev *pdev,
198                         struct cxl_device_regs *regs,
199                         struct cxl_register_map *map)
200 {
201         struct device *dev = &pdev->dev;
202         resource_size_t phys_addr;
203
204         phys_addr = pci_resource_start(pdev, map->barno);
205         phys_addr += map->block_offset;
206
207         if (map->device_map.status.valid) {
208                 resource_size_t addr;
209                 resource_size_t length;
210
211                 addr = phys_addr + map->device_map.status.offset;
212                 length = map->device_map.status.size;
213                 regs->status = devm_cxl_iomap_block(dev, addr, length);
214                 if (!regs->status)
215                         return -ENOMEM;
216         }
217
218         if (map->device_map.mbox.valid) {
219                 resource_size_t addr;
220                 resource_size_t length;
221
222                 addr = phys_addr + map->device_map.mbox.offset;
223                 length = map->device_map.mbox.size;
224                 regs->mbox = devm_cxl_iomap_block(dev, addr, length);
225                 if (!regs->mbox)
226                         return -ENOMEM;
227         }
228
229         if (map->device_map.memdev.valid) {
230                 resource_size_t addr;
231                 resource_size_t length;
232
233                 addr = phys_addr + map->device_map.memdev.offset;
234                 length = map->device_map.memdev.size;
235                 regs->memdev = devm_cxl_iomap_block(dev, addr, length);
236                 if (!regs->memdev)
237                         return -ENOMEM;
238         }
239
240         return 0;
241 }
242 EXPORT_SYMBOL_GPL(cxl_map_device_regs);
243
244 struct bus_type cxl_bus_type = {
245         .name = "cxl",
246 };
247 EXPORT_SYMBOL_GPL(cxl_bus_type);
248
249 static __init int cxl_core_init(void)
250 {
251         return bus_register(&cxl_bus_type);
252 }
253
254 static void cxl_core_exit(void)
255 {
256         bus_unregister(&cxl_bus_type);
257 }
258
259 module_init(cxl_core_init);
260 module_exit(cxl_core_exit);
261 MODULE_LICENSE("GPL v2");