diff options
| -rw-r--r-- | drivers/acpi/nfit.c | 97 | ||||
| -rw-r--r-- | drivers/acpi/nfit.h | 15 |
2 files changed, 100 insertions, 12 deletions
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index a20b7c883ca0..11aa513f2fda 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/list.h> | 18 | #include <linux/list.h> |
| 19 | #include <linux/acpi.h> | 19 | #include <linux/acpi.h> |
| 20 | #include <linux/sort.h> | 20 | #include <linux/sort.h> |
| 21 | #include <linux/pmem.h> | ||
| 21 | #include <linux/io.h> | 22 | #include <linux/io.h> |
| 22 | #include "nfit.h" | 23 | #include "nfit.h" |
| 23 | 24 | ||
| @@ -305,6 +306,23 @@ static bool add_idt(struct acpi_nfit_desc *acpi_desc, | |||
| 305 | return true; | 306 | return true; |
| 306 | } | 307 | } |
| 307 | 308 | ||
| 309 | static bool add_flush(struct acpi_nfit_desc *acpi_desc, | ||
| 310 | struct acpi_nfit_flush_address *flush) | ||
| 311 | { | ||
| 312 | struct device *dev = acpi_desc->dev; | ||
| 313 | struct nfit_flush *nfit_flush = devm_kzalloc(dev, sizeof(*nfit_flush), | ||
| 314 | GFP_KERNEL); | ||
| 315 | |||
| 316 | if (!nfit_flush) | ||
| 317 | return false; | ||
| 318 | INIT_LIST_HEAD(&nfit_flush->list); | ||
| 319 | nfit_flush->flush = flush; | ||
| 320 | list_add_tail(&nfit_flush->list, &acpi_desc->flushes); | ||
| 321 | dev_dbg(dev, "%s: nfit_flush handle: %d hint_count: %d\n", __func__, | ||
| 322 | flush->device_handle, flush->hint_count); | ||
| 323 | return true; | ||
| 324 | } | ||
| 325 | |||
| 308 | static void *add_table(struct acpi_nfit_desc *acpi_desc, void *table, | 326 | static void *add_table(struct acpi_nfit_desc *acpi_desc, void *table, |
| 309 | const void *end) | 327 | const void *end) |
| 310 | { | 328 | { |
| @@ -338,7 +356,8 @@ static void *add_table(struct acpi_nfit_desc *acpi_desc, void *table, | |||
| 338 | return err; | 356 | return err; |
| 339 | break; | 357 | break; |
| 340 | case ACPI_NFIT_TYPE_FLUSH_ADDRESS: | 358 | case ACPI_NFIT_TYPE_FLUSH_ADDRESS: |
| 341 | dev_dbg(dev, "%s: flush\n", __func__); | 359 | if (!add_flush(acpi_desc, table)) |
| 360 | return err; | ||
| 342 | break; | 361 | break; |
| 343 | case ACPI_NFIT_TYPE_SMBIOS: | 362 | case ACPI_NFIT_TYPE_SMBIOS: |
| 344 | dev_dbg(dev, "%s: smbios\n", __func__); | 363 | dev_dbg(dev, "%s: smbios\n", __func__); |
| @@ -389,6 +408,7 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, | |||
| 389 | { | 408 | { |
| 390 | u16 dcr = __to_nfit_memdev(nfit_mem)->region_index; | 409 | u16 dcr = __to_nfit_memdev(nfit_mem)->region_index; |
| 391 | struct nfit_memdev *nfit_memdev; | 410 | struct nfit_memdev *nfit_memdev; |
| 411 | struct nfit_flush *nfit_flush; | ||
| 392 | struct nfit_dcr *nfit_dcr; | 412 | struct nfit_dcr *nfit_dcr; |
| 393 | struct nfit_bdw *nfit_bdw; | 413 | struct nfit_bdw *nfit_bdw; |
| 394 | struct nfit_idt *nfit_idt; | 414 | struct nfit_idt *nfit_idt; |
| @@ -442,6 +462,14 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, | |||
| 442 | nfit_mem->idt_bdw = nfit_idt->idt; | 462 | nfit_mem->idt_bdw = nfit_idt->idt; |
| 443 | break; | 463 | break; |
| 444 | } | 464 | } |
| 465 | |||
| 466 | list_for_each_entry(nfit_flush, &acpi_desc->flushes, list) { | ||
| 467 | if (nfit_flush->flush->device_handle != | ||
| 468 | nfit_memdev->memdev->device_handle) | ||
| 469 | continue; | ||
| 470 | nfit_mem->nfit_flush = nfit_flush; | ||
| 471 | break; | ||
| 472 | } | ||
| 445 | break; | 473 | break; |
| 446 | } | 474 | } |
| 447 | 475 | ||
| @@ -978,6 +1006,24 @@ static u64 to_interleave_offset(u64 offset, struct nfit_blk_mmio *mmio) | |||
| 978 | return mmio->base_offset + line_offset + table_offset + sub_line_offset; | 1006 | return mmio->base_offset + line_offset + table_offset + sub_line_offset; |
| 979 | } | 1007 | } |
| 980 | 1008 | ||
| 1009 | static void wmb_blk(struct nfit_blk *nfit_blk) | ||
| 1010 | { | ||
| 1011 | |||
| 1012 | if (nfit_blk->nvdimm_flush) { | ||
| 1013 | /* | ||
| 1014 | * The first wmb() is needed to 'sfence' all previous writes | ||
| 1015 | * such that they are architecturally visible for the platform | ||
| 1016 | * buffer flush. Note that we've already arranged for pmem | ||
| 1017 | * writes to avoid the cache via arch_memcpy_to_pmem(). The | ||
| 1018 | * final wmb() ensures ordering for the NVDIMM flush write. | ||
| 1019 | */ | ||
| 1020 | wmb(); | ||
| 1021 | writeq(1, nfit_blk->nvdimm_flush); | ||
| 1022 | wmb(); | ||
| 1023 | } else | ||
| 1024 | wmb_pmem(); | ||
| 1025 | } | ||
| 1026 | |||
| 981 | static u64 read_blk_stat(struct nfit_blk *nfit_blk, unsigned int bw) | 1027 | static u64 read_blk_stat(struct nfit_blk *nfit_blk, unsigned int bw) |
| 982 | { | 1028 | { |
| 983 | struct nfit_blk_mmio *mmio = &nfit_blk->mmio[DCR]; | 1029 | struct nfit_blk_mmio *mmio = &nfit_blk->mmio[DCR]; |
| @@ -1012,6 +1058,7 @@ static void write_blk_ctl(struct nfit_blk *nfit_blk, unsigned int bw, | |||
| 1012 | offset = to_interleave_offset(offset, mmio); | 1058 | offset = to_interleave_offset(offset, mmio); |
| 1013 | 1059 | ||
| 1014 | writeq(cmd, mmio->base + offset); | 1060 | writeq(cmd, mmio->base + offset); |
| 1061 | wmb_blk(nfit_blk); | ||
| 1015 | /* FIXME: conditionally perform read-back if mandated by firmware */ | 1062 | /* FIXME: conditionally perform read-back if mandated by firmware */ |
| 1016 | } | 1063 | } |
| 1017 | 1064 | ||
| @@ -1026,7 +1073,6 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk, | |||
| 1026 | 1073 | ||
| 1027 | base_offset = nfit_blk->bdw_offset + dpa % L1_CACHE_BYTES | 1074 | base_offset = nfit_blk->bdw_offset + dpa % L1_CACHE_BYTES |
| 1028 | + lane * mmio->size; | 1075 | + lane * mmio->size; |
| 1029 | /* TODO: non-temporal access, flush hints, cache management etc... */ | ||
| 1030 | write_blk_ctl(nfit_blk, lane, dpa, len, rw); | 1076 | write_blk_ctl(nfit_blk, lane, dpa, len, rw); |
| 1031 | while (len) { | 1077 | while (len) { |
| 1032 | unsigned int c; | 1078 | unsigned int c; |
| @@ -1045,13 +1091,19 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk, | |||
| 1045 | } | 1091 | } |
| 1046 | 1092 | ||
| 1047 | if (rw) | 1093 | if (rw) |
| 1048 | memcpy(mmio->aperture + offset, iobuf + copied, c); | 1094 | memcpy_to_pmem(mmio->aperture + offset, |
| 1095 | iobuf + copied, c); | ||
| 1049 | else | 1096 | else |
| 1050 | memcpy(iobuf + copied, mmio->aperture + offset, c); | 1097 | memcpy_from_pmem(iobuf + copied, |
| 1098 | mmio->aperture + offset, c); | ||
| 1051 | 1099 | ||
| 1052 | copied += c; | 1100 | copied += c; |
| 1053 | len -= c; | 1101 | len -= c; |
| 1054 | } | 1102 | } |
| 1103 | |||
| 1104 | if (rw) | ||
| 1105 | wmb_blk(nfit_blk); | ||
| 1106 | |||
| 1055 | rc = read_blk_stat(nfit_blk, lane) ? -EIO : 0; | 1107 | rc = read_blk_stat(nfit_blk, lane) ? -EIO : 0; |
| 1056 | return rc; | 1108 | return rc; |
| 1057 | } | 1109 | } |
| @@ -1124,7 +1176,7 @@ static void nfit_spa_unmap(struct acpi_nfit_desc *acpi_desc, | |||
| 1124 | } | 1176 | } |
| 1125 | 1177 | ||
| 1126 | static void __iomem *__nfit_spa_map(struct acpi_nfit_desc *acpi_desc, | 1178 | static void __iomem *__nfit_spa_map(struct acpi_nfit_desc *acpi_desc, |
| 1127 | struct acpi_nfit_system_address *spa) | 1179 | struct acpi_nfit_system_address *spa, enum spa_map_type type) |
| 1128 | { | 1180 | { |
| 1129 | resource_size_t start = spa->address; | 1181 | resource_size_t start = spa->address; |
| 1130 | resource_size_t n = spa->length; | 1182 | resource_size_t n = spa->length; |
| @@ -1152,8 +1204,15 @@ static void __iomem *__nfit_spa_map(struct acpi_nfit_desc *acpi_desc, | |||
| 1152 | if (!res) | 1204 | if (!res) |
| 1153 | goto err_mem; | 1205 | goto err_mem; |
| 1154 | 1206 | ||
| 1155 | /* TODO: cacheability based on the spa type */ | 1207 | if (type == SPA_MAP_APERTURE) { |
| 1156 | spa_map->iomem = ioremap_nocache(start, n); | 1208 | /* |
| 1209 | * TODO: memremap_pmem() support, but that requires cache | ||
| 1210 | * flushing when the aperture is moved. | ||
| 1211 | */ | ||
| 1212 | spa_map->iomem = ioremap_wc(start, n); | ||
| 1213 | } else | ||
| 1214 | spa_map->iomem = ioremap_nocache(start, n); | ||
| 1215 | |||
| 1157 | if (!spa_map->iomem) | 1216 | if (!spa_map->iomem) |
| 1158 | goto err_map; | 1217 | goto err_map; |
| 1159 | 1218 | ||
| @@ -1171,6 +1230,7 @@ static void __iomem *__nfit_spa_map(struct acpi_nfit_desc *acpi_desc, | |||
| 1171 | * nfit_spa_map - interleave-aware managed-mappings of acpi_nfit_system_address ranges | 1230 | * nfit_spa_map - interleave-aware managed-mappings of acpi_nfit_system_address ranges |
| 1172 | * @nvdimm_bus: NFIT-bus that provided the spa table entry | 1231 | * @nvdimm_bus: NFIT-bus that provided the spa table entry |
| 1173 | * @nfit_spa: spa table to map | 1232 | * @nfit_spa: spa table to map |
| 1233 | * @type: aperture or control region | ||
| 1174 | * | 1234 | * |
| 1175 | * In the case where block-data-window apertures and | 1235 | * In the case where block-data-window apertures and |
| 1176 | * dimm-control-regions are interleaved they will end up sharing a | 1236 | * dimm-control-regions are interleaved they will end up sharing a |
| @@ -1180,12 +1240,12 @@ static void __iomem *__nfit_spa_map(struct acpi_nfit_desc *acpi_desc, | |||
| 1180 | * unbound. | 1240 | * unbound. |
| 1181 | */ | 1241 | */ |
| 1182 | static void __iomem *nfit_spa_map(struct acpi_nfit_desc *acpi_desc, | 1242 | static void __iomem *nfit_spa_map(struct acpi_nfit_desc *acpi_desc, |
| 1183 | struct acpi_nfit_system_address *spa) | 1243 | struct acpi_nfit_system_address *spa, enum spa_map_type type) |
| 1184 | { | 1244 | { |
| 1185 | void __iomem *iomem; | 1245 | void __iomem *iomem; |
| 1186 | 1246 | ||
| 1187 | mutex_lock(&acpi_desc->spa_map_mutex); | 1247 | mutex_lock(&acpi_desc->spa_map_mutex); |
| 1188 | iomem = __nfit_spa_map(acpi_desc, spa); | 1248 | iomem = __nfit_spa_map(acpi_desc, spa, type); |
| 1189 | mutex_unlock(&acpi_desc->spa_map_mutex); | 1249 | mutex_unlock(&acpi_desc->spa_map_mutex); |
| 1190 | 1250 | ||
| 1191 | return iomem; | 1251 | return iomem; |
| @@ -1212,6 +1272,7 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, | |||
| 1212 | struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); | 1272 | struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus); |
| 1213 | struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); | 1273 | struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc); |
| 1214 | struct nd_blk_region *ndbr = to_nd_blk_region(dev); | 1274 | struct nd_blk_region *ndbr = to_nd_blk_region(dev); |
| 1275 | struct nfit_flush *nfit_flush; | ||
| 1215 | struct nfit_blk_mmio *mmio; | 1276 | struct nfit_blk_mmio *mmio; |
| 1216 | struct nfit_blk *nfit_blk; | 1277 | struct nfit_blk *nfit_blk; |
| 1217 | struct nfit_mem *nfit_mem; | 1278 | struct nfit_mem *nfit_mem; |
| @@ -1237,7 +1298,8 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, | |||
| 1237 | /* map block aperture memory */ | 1298 | /* map block aperture memory */ |
| 1238 | nfit_blk->bdw_offset = nfit_mem->bdw->offset; | 1299 | nfit_blk->bdw_offset = nfit_mem->bdw->offset; |
| 1239 | mmio = &nfit_blk->mmio[BDW]; | 1300 | mmio = &nfit_blk->mmio[BDW]; |
| 1240 | mmio->base = nfit_spa_map(acpi_desc, nfit_mem->spa_bdw); | 1301 | mmio->base = nfit_spa_map(acpi_desc, nfit_mem->spa_bdw, |
| 1302 | SPA_MAP_APERTURE); | ||
| 1241 | if (!mmio->base) { | 1303 | if (!mmio->base) { |
| 1242 | dev_dbg(dev, "%s: %s failed to map bdw\n", __func__, | 1304 | dev_dbg(dev, "%s: %s failed to map bdw\n", __func__, |
| 1243 | nvdimm_name(nvdimm)); | 1305 | nvdimm_name(nvdimm)); |
| @@ -1259,7 +1321,8 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, | |||
| 1259 | nfit_blk->cmd_offset = nfit_mem->dcr->command_offset; | 1321 | nfit_blk->cmd_offset = nfit_mem->dcr->command_offset; |
| 1260 | nfit_blk->stat_offset = nfit_mem->dcr->status_offset; | 1322 | nfit_blk->stat_offset = nfit_mem->dcr->status_offset; |
| 1261 | mmio = &nfit_blk->mmio[DCR]; | 1323 | mmio = &nfit_blk->mmio[DCR]; |
| 1262 | mmio->base = nfit_spa_map(acpi_desc, nfit_mem->spa_dcr); | 1324 | mmio->base = nfit_spa_map(acpi_desc, nfit_mem->spa_dcr, |
| 1325 | SPA_MAP_CONTROL); | ||
| 1263 | if (!mmio->base) { | 1326 | if (!mmio->base) { |
| 1264 | dev_dbg(dev, "%s: %s failed to map dcr\n", __func__, | 1327 | dev_dbg(dev, "%s: %s failed to map dcr\n", __func__, |
| 1265 | nvdimm_name(nvdimm)); | 1328 | nvdimm_name(nvdimm)); |
| @@ -1277,6 +1340,17 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus, | |||
| 1277 | return rc; | 1340 | return rc; |
| 1278 | } | 1341 | } |
| 1279 | 1342 | ||
| 1343 | nfit_flush = nfit_mem->nfit_flush; | ||
| 1344 | if (nfit_flush && nfit_flush->flush->hint_count != 0) { | ||
| 1345 | nfit_blk->nvdimm_flush = devm_ioremap_nocache(dev, | ||
| 1346 | nfit_flush->flush->hint_address[0], 8); | ||
| 1347 | if (!nfit_blk->nvdimm_flush) | ||
| 1348 | return -ENOMEM; | ||
| 1349 | } | ||
| 1350 | |||
| 1351 | if (!arch_has_pmem_api() && !nfit_blk->nvdimm_flush) | ||
| 1352 | dev_warn(dev, "unable to guarantee persistence of writes\n"); | ||
| 1353 | |||
| 1280 | if (mmio->line_size == 0) | 1354 | if (mmio->line_size == 0) |
| 1281 | return 0; | 1355 | return 0; |
| 1282 | 1356 | ||
| @@ -1459,6 +1533,7 @@ int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz) | |||
| 1459 | INIT_LIST_HEAD(&acpi_desc->dcrs); | 1533 | INIT_LIST_HEAD(&acpi_desc->dcrs); |
| 1460 | INIT_LIST_HEAD(&acpi_desc->bdws); | 1534 | INIT_LIST_HEAD(&acpi_desc->bdws); |
| 1461 | INIT_LIST_HEAD(&acpi_desc->idts); | 1535 | INIT_LIST_HEAD(&acpi_desc->idts); |
| 1536 | INIT_LIST_HEAD(&acpi_desc->flushes); | ||
| 1462 | INIT_LIST_HEAD(&acpi_desc->memdevs); | 1537 | INIT_LIST_HEAD(&acpi_desc->memdevs); |
| 1463 | INIT_LIST_HEAD(&acpi_desc->dimms); | 1538 | INIT_LIST_HEAD(&acpi_desc->dimms); |
| 1464 | mutex_init(&acpi_desc->spa_map_mutex); | 1539 | mutex_init(&acpi_desc->spa_map_mutex); |
diff --git a/drivers/acpi/nfit.h b/drivers/acpi/nfit.h index 81f2e8c5a79c..815cb561cdba 100644 --- a/drivers/acpi/nfit.h +++ b/drivers/acpi/nfit.h | |||
| @@ -60,6 +60,11 @@ struct nfit_idt { | |||
| 60 | struct list_head list; | 60 | struct list_head list; |
| 61 | }; | 61 | }; |
| 62 | 62 | ||
| 63 | struct nfit_flush { | ||
| 64 | struct acpi_nfit_flush_address *flush; | ||
| 65 | struct list_head list; | ||
| 66 | }; | ||
| 67 | |||
| 63 | struct nfit_memdev { | 68 | struct nfit_memdev { |
| 64 | struct acpi_nfit_memory_map *memdev; | 69 | struct acpi_nfit_memory_map *memdev; |
| 65 | struct list_head list; | 70 | struct list_head list; |
| @@ -77,6 +82,7 @@ struct nfit_mem { | |||
| 77 | struct acpi_nfit_system_address *spa_bdw; | 82 | struct acpi_nfit_system_address *spa_bdw; |
| 78 | struct acpi_nfit_interleave *idt_dcr; | 83 | struct acpi_nfit_interleave *idt_dcr; |
| 79 | struct acpi_nfit_interleave *idt_bdw; | 84 | struct acpi_nfit_interleave *idt_bdw; |
| 85 | struct nfit_flush *nfit_flush; | ||
| 80 | struct list_head list; | 86 | struct list_head list; |
| 81 | struct acpi_device *adev; | 87 | struct acpi_device *adev; |
| 82 | unsigned long dsm_mask; | 88 | unsigned long dsm_mask; |
| @@ -88,6 +94,7 @@ struct acpi_nfit_desc { | |||
| 88 | struct mutex spa_map_mutex; | 94 | struct mutex spa_map_mutex; |
| 89 | struct list_head spa_maps; | 95 | struct list_head spa_maps; |
| 90 | struct list_head memdevs; | 96 | struct list_head memdevs; |
| 97 | struct list_head flushes; | ||
| 91 | struct list_head dimms; | 98 | struct list_head dimms; |
| 92 | struct list_head spas; | 99 | struct list_head spas; |
| 93 | struct list_head dcrs; | 100 | struct list_head dcrs; |
| @@ -109,7 +116,7 @@ struct nfit_blk { | |||
| 109 | struct nfit_blk_mmio { | 116 | struct nfit_blk_mmio { |
| 110 | union { | 117 | union { |
| 111 | void __iomem *base; | 118 | void __iomem *base; |
| 112 | void *aperture; | 119 | void __pmem *aperture; |
| 113 | }; | 120 | }; |
| 114 | u64 size; | 121 | u64 size; |
| 115 | u64 base_offset; | 122 | u64 base_offset; |
| @@ -123,6 +130,12 @@ struct nfit_blk { | |||
| 123 | u64 bdw_offset; /* post interleave offset */ | 130 | u64 bdw_offset; /* post interleave offset */ |
| 124 | u64 stat_offset; | 131 | u64 stat_offset; |
| 125 | u64 cmd_offset; | 132 | u64 cmd_offset; |
| 133 | void __iomem *nvdimm_flush; | ||
| 134 | }; | ||
| 135 | |||
| 136 | enum spa_map_type { | ||
| 137 | SPA_MAP_CONTROL, | ||
| 138 | SPA_MAP_APERTURE, | ||
| 126 | }; | 139 | }; |
| 127 | 140 | ||
| 128 | struct nfit_spa_mapping { | 141 | struct nfit_spa_mapping { |
