acpi/hmat: Parse and report heterogeneous memory
[linux-2.6-microblaze.git] / drivers / acpi / hmat / hmat.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2019, Intel Corporation.
4  *
5  * Heterogeneous Memory Attributes Table (HMAT) representation
6  *
7  * This program parses and reports the platform's HMAT tables, and registers
8  * the applicable attributes with the node's interfaces.
9  */
10
11 #include <linux/acpi.h>
12 #include <linux/bitops.h>
13 #include <linux/device.h>
14 #include <linux/init.h>
15 #include <linux/list.h>
16 #include <linux/node.h>
17 #include <linux/sysfs.h>
18
19 static __initdata u8 hmat_revision;
20
21 static __init const char *hmat_data_type(u8 type)
22 {
23         switch (type) {
24         case ACPI_HMAT_ACCESS_LATENCY:
25                 return "Access Latency";
26         case ACPI_HMAT_READ_LATENCY:
27                 return "Read Latency";
28         case ACPI_HMAT_WRITE_LATENCY:
29                 return "Write Latency";
30         case ACPI_HMAT_ACCESS_BANDWIDTH:
31                 return "Access Bandwidth";
32         case ACPI_HMAT_READ_BANDWIDTH:
33                 return "Read Bandwidth";
34         case ACPI_HMAT_WRITE_BANDWIDTH:
35                 return "Write Bandwidth";
36         default:
37                 return "Reserved";
38         }
39 }
40
41 static __init const char *hmat_data_type_suffix(u8 type)
42 {
43         switch (type) {
44         case ACPI_HMAT_ACCESS_LATENCY:
45         case ACPI_HMAT_READ_LATENCY:
46         case ACPI_HMAT_WRITE_LATENCY:
47                 return " nsec";
48         case ACPI_HMAT_ACCESS_BANDWIDTH:
49         case ACPI_HMAT_READ_BANDWIDTH:
50         case ACPI_HMAT_WRITE_BANDWIDTH:
51                 return " MB/s";
52         default:
53                 return "";
54         }
55 }
56
57 static __init u32 hmat_normalize(u16 entry, u64 base, u8 type)
58 {
59         u32 value;
60
61         /*
62          * Check for invalid and overflow values
63          */
64         if (entry == 0xffff || !entry)
65                 return 0;
66         else if (base > (UINT_MAX / (entry)))
67                 return 0;
68
69         /*
70          * Divide by the base unit for version 1, convert latency from
71          * picosenonds to nanoseconds if revision 2.
72          */
73         value = entry * base;
74         if (hmat_revision == 1) {
75                 if (value < 10)
76                         return 0;
77                 value = DIV_ROUND_UP(value, 10);
78         } else if (hmat_revision == 2) {
79                 switch (type) {
80                 case ACPI_HMAT_ACCESS_LATENCY:
81                 case ACPI_HMAT_READ_LATENCY:
82                 case ACPI_HMAT_WRITE_LATENCY:
83                         value = DIV_ROUND_UP(value, 1000);
84                         break;
85                 default:
86                         break;
87                 }
88         }
89         return value;
90 }
91
92 static __init int hmat_parse_locality(union acpi_subtable_headers *header,
93                                       const unsigned long end)
94 {
95         struct acpi_hmat_locality *hmat_loc = (void *)header;
96         unsigned int init, targ, total_size, ipds, tpds;
97         u32 *inits, *targs, value;
98         u16 *entries;
99         u8 type;
100
101         if (hmat_loc->header.length < sizeof(*hmat_loc)) {
102                 pr_notice("HMAT: Unexpected locality header length: %d\n",
103                          hmat_loc->header.length);
104                 return -EINVAL;
105         }
106
107         type = hmat_loc->data_type;
108         ipds = hmat_loc->number_of_initiator_Pds;
109         tpds = hmat_loc->number_of_target_Pds;
110         total_size = sizeof(*hmat_loc) + sizeof(*entries) * ipds * tpds +
111                      sizeof(*inits) * ipds + sizeof(*targs) * tpds;
112         if (hmat_loc->header.length < total_size) {
113                 pr_notice("HMAT: Unexpected locality header length:%d, minimum required:%d\n",
114                          hmat_loc->header.length, total_size);
115                 return -EINVAL;
116         }
117
118         pr_info("HMAT: Locality: Flags:%02x Type:%s Initiator Domains:%d Target Domains:%d Base:%lld\n",
119                 hmat_loc->flags, hmat_data_type(type), ipds, tpds,
120                 hmat_loc->entry_base_unit);
121
122         inits = (u32 *)(hmat_loc + 1);
123         targs = inits + ipds;
124         entries = (u16 *)(targs + tpds);
125         for (init = 0; init < ipds; init++) {
126                 for (targ = 0; targ < tpds; targ++) {
127                         value = hmat_normalize(entries[init * tpds + targ],
128                                                hmat_loc->entry_base_unit,
129                                                type);
130                         pr_info("  Initiator-Target[%d-%d]:%d%s\n",
131                                 inits[init], targs[targ], value,
132                                 hmat_data_type_suffix(type));
133                 }
134         }
135
136         return 0;
137 }
138
139 static __init int hmat_parse_cache(union acpi_subtable_headers *header,
140                                    const unsigned long end)
141 {
142         struct acpi_hmat_cache *cache = (void *)header;
143         u32 attrs;
144
145         if (cache->header.length < sizeof(*cache)) {
146                 pr_notice("HMAT: Unexpected cache header length: %d\n",
147                          cache->header.length);
148                 return -EINVAL;
149         }
150
151         attrs = cache->cache_attributes;
152         pr_info("HMAT: Cache: Domain:%d Size:%llu Attrs:%08x SMBIOS Handles:%d\n",
153                 cache->memory_PD, cache->cache_size, attrs,
154                 cache->number_of_SMBIOShandles);
155
156         return 0;
157 }
158
159 static int __init hmat_parse_proximity_domain(union acpi_subtable_headers *header,
160                                               const unsigned long end)
161 {
162         struct acpi_hmat_proximity_domain *p = (void *)header;
163
164         if (p->header.length != sizeof(*p)) {
165                 pr_notice("HMAT: Unexpected address range header length: %d\n",
166                          p->header.length);
167                 return -EINVAL;
168         }
169
170         if (hmat_revision == 1)
171                 pr_info("HMAT: Memory (%#llx length %#llx) Flags:%04x Processor Domain:%d Memory Domain:%d\n",
172                         p->reserved3, p->reserved4, p->flags, p->processor_PD,
173                         p->memory_PD);
174         else
175                 pr_info("HMAT: Memory Flags:%04x Processor Domain:%d Memory Domain:%d\n",
176                         p->flags, p->processor_PD, p->memory_PD);
177
178         return 0;
179 }
180
181 static int __init hmat_parse_subtable(union acpi_subtable_headers *header,
182                                       const unsigned long end)
183 {
184         struct acpi_hmat_structure *hdr = (void *)header;
185
186         if (!hdr)
187                 return -EINVAL;
188
189         switch (hdr->type) {
190         case ACPI_HMAT_TYPE_ADDRESS_RANGE:
191                 return hmat_parse_proximity_domain(header, end);
192         case ACPI_HMAT_TYPE_LOCALITY:
193                 return hmat_parse_locality(header, end);
194         case ACPI_HMAT_TYPE_CACHE:
195                 return hmat_parse_cache(header, end);
196         default:
197                 return -EINVAL;
198         }
199 }
200
201 static __init int hmat_init(void)
202 {
203         struct acpi_table_header *tbl;
204         enum acpi_hmat_type i;
205         acpi_status status;
206
207         if (srat_disabled())
208                 return 0;
209
210         status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl);
211         if (ACPI_FAILURE(status))
212                 return 0;
213
214         hmat_revision = tbl->revision;
215         switch (hmat_revision) {
216         case 1:
217         case 2:
218                 break;
219         default:
220                 pr_notice("Ignoring HMAT: Unknown revision:%d\n", hmat_revision);
221                 goto out_put;
222         }
223
224         for (i = ACPI_HMAT_TYPE_ADDRESS_RANGE; i < ACPI_HMAT_TYPE_RESERVED; i++) {
225                 if (acpi_table_parse_entries(ACPI_SIG_HMAT,
226                                              sizeof(struct acpi_table_hmat), i,
227                                              hmat_parse_subtable, 0) < 0) {
228                         pr_notice("Ignoring HMAT: Invalid table");
229                         goto out_put;
230                 }
231         }
232 out_put:
233         acpi_put_table(tbl);
234         return 0;
235 }
236 subsys_initcall(hmat_init);