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