162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2014-2016, Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include "test/nfit_test.h"
662306a36Sopenharmony_ci#include <linux/blkdev.h>
762306a36Sopenharmony_ci#include <linux/dax.h>
862306a36Sopenharmony_ci#include <pmem.h>
962306a36Sopenharmony_ci#include <nd.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cilong __pmem_direct_access(struct pmem_device *pmem, pgoff_t pgoff,
1262306a36Sopenharmony_ci		long nr_pages, enum dax_access_mode mode, void **kaddr,
1362306a36Sopenharmony_ci		pfn_t *pfn)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	resource_size_t offset = PFN_PHYS(pgoff) + pmem->data_offset;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	if (unlikely(is_bad_pmem(&pmem->bb, PFN_PHYS(pgoff) / 512,
1862306a36Sopenharmony_ci					PFN_PHYS(nr_pages))))
1962306a36Sopenharmony_ci		return -EIO;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	/*
2262306a36Sopenharmony_ci	 * Limit dax to a single page at a time given vmalloc()-backed
2362306a36Sopenharmony_ci	 * in the nfit_test case.
2462306a36Sopenharmony_ci	 */
2562306a36Sopenharmony_ci	if (get_nfit_res(pmem->phys_addr + offset)) {
2662306a36Sopenharmony_ci		struct page *page;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci		if (kaddr)
2962306a36Sopenharmony_ci			*kaddr = pmem->virt_addr + offset;
3062306a36Sopenharmony_ci		page = vmalloc_to_page(pmem->virt_addr + offset);
3162306a36Sopenharmony_ci		if (pfn)
3262306a36Sopenharmony_ci			*pfn = page_to_pfn_t(page);
3362306a36Sopenharmony_ci		pr_debug_ratelimited("%s: pmem: %p pgoff: %#lx pfn: %#lx\n",
3462306a36Sopenharmony_ci				__func__, pmem, pgoff, page_to_pfn(page));
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		return 1;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (kaddr)
4062306a36Sopenharmony_ci		*kaddr = pmem->virt_addr + offset;
4162306a36Sopenharmony_ci	if (pfn)
4262306a36Sopenharmony_ci		*pfn = phys_to_pfn_t(pmem->phys_addr + offset, pmem->pfn_flags);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/*
4562306a36Sopenharmony_ci	 * If badblocks are present, limit known good range to the
4662306a36Sopenharmony_ci	 * requested range.
4762306a36Sopenharmony_ci	 */
4862306a36Sopenharmony_ci	if (unlikely(pmem->bb.count))
4962306a36Sopenharmony_ci		return nr_pages;
5062306a36Sopenharmony_ci	return PHYS_PFN(pmem->size - pmem->pfn_pad - offset);
5162306a36Sopenharmony_ci}
52