blob: 812a05c5d9fb22cd03999c2010eae5f0478f13da [file] [log] [blame] [edit]
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Edge TPU MMU API.
*
* Copyright (C) 2020 Google, Inc.
*/
#ifndef __EDGETPU_MMU_H__
#define __EDGETPU_MMU_H__
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
#include <linux/iommu.h>
#include <linux/scatterlist.h>
#include <linux/version.h>
#include "edgetpu-internal.h"
#include "edgetpu.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)
#ifndef IOMMU_PASID_INVALID
#define IOMMU_PASID_INVALID (-1U)
#endif
#endif
#define IS_MIRRORED(flag) (!((flag) & EDGETPU_MAP_NONMIRRORED))
/* flags for MMU operations */
/* The target of operation is a single die or the whole virtual device group */
#define EDGETPU_MMU_VDG (0 << 0)
#define EDGETPU_MMU_DIE (1 << 0)
/* Whether the TPU address to be allocated can be 64-bit wide. */
#define EDGETPU_MMU_32 (0 << 1)
#define EDGETPU_MMU_64 (1 << 1)
/* The memory will be mapped to host DRAM, device DRAM, or dma-buf. */
#define EDGETPU_MMU_HOST (0 << 2)
#define EDGETPU_MMU_DEVICE (1 << 2)
#define EDGETPU_MMU_DMABUF (2 << 2)
#define EDGETPU_MMU_COHERENT (1 << 4)
/*
* The max possible value of token is (EDGETPU_DOMAIN_TOKEN_END - 1), which
* shouldn't equal or exceed the bit mask EDGETPU_CONTEXT_DOMAIN_TOKEN.
*/
#define EDGETPU_DOMAIN_TOKEN_END EDGETPU_CONTEXT_DOMAIN_TOKEN
struct edgetpu_iommu_domain {
/*
* IOMMU PASID, set by edgetpu_mmu_attach_domain().
* This field should be set as IOMMU_PASID_INVALID in
* edgetpu_mmu_detach_domain().
*/
uint pasid;
struct iommu_domain *iommu_domain;
/*
* A token set by edgetpu_mmu_alloc_domain(). See the description of
* edgetpu_mmu_add_translation() about @context_id for more details.
*/
int token;
};
/*
* Return the DMA direction to use for the host DMA API call to map a buffer.
* Normally DMA buffers "only written" by the device (so far as the TPU runtime
* is concerned) would be mapped write-only to the host IOMMU. However, our
* TPU CPU may perform cache line fills and possibly prefetches from the buffer
* being written to. Map write-only buffers bi-directional.
*/
static inline enum dma_data_direction
edgetpu_host_dma_dir(enum dma_data_direction target_dir)
{
switch (target_dir) {
case DMA_FROM_DEVICE:
return DMA_BIDIRECTIONAL;
default:
return target_dir;
}
}
static inline enum dma_data_direction map_flag_to_host_dma_dir(edgetpu_map_flag_t flags)
{
return edgetpu_host_dma_dir(flags & EDGETPU_MAP_DIR_MASK);
}
static inline u32 map_to_mmu_flags(edgetpu_map_flag_t flags)
{
u32 ret = 0;
ret |= IS_MIRRORED(flags) ? EDGETPU_MMU_VDG : EDGETPU_MMU_DIE;
ret |= (flags & EDGETPU_MAP_CPU_NONACCESSIBLE) ? EDGETPU_MMU_64 :
EDGETPU_MMU_32;
ret |= (flags & EDGETPU_MAP_COHERENT) ? EDGETPU_MMU_COHERENT : 0;
return ret;
}
/* To be compatible with Linux kernel without this flag. */
#ifndef DMA_ATTR_PBHA_PROT
#define DMA_ATTR_PBHA_PROT(x) 0
#endif
#ifndef IOMMU_PBHA_PROT
#define IOMMU_PBHA_PROT(x) 0
#endif
/* fetch the value of PBHA in map flags */
#define EDGEPTU_MAP_PBHA_VALUE(flags) \
((flags >> EDGETPU_MAP_ATTR_PBHA_SHIFT) & EDGETPU_MAP_ATTR_PBHA_MASK)
/*
* Converts edgetpu map flag to DMA attr.
*
* Ignore EDGETPU_MAP_SKIP_CPU_SYNC if @map = true
*/
static inline unsigned long map_to_dma_attr(edgetpu_map_flag_t flags, bool map)
{
unsigned long attr = 0;
if (!map && flags & EDGETPU_MAP_SKIP_CPU_SYNC)
attr = DMA_ATTR_SKIP_CPU_SYNC;
attr |= DMA_ATTR_PBHA_PROT(EDGEPTU_MAP_PBHA_VALUE(flags));
return attr;
}
int edgetpu_mmu_attach(struct edgetpu_dev *dev, void *mmu_info);
void edgetpu_mmu_detach(struct edgetpu_dev *dev);
/**
* Re-attach to previously attached MMU.
*
* Description: Re-attach to previously attached MMU with the same @mmu_info
* passed in edgetpu_mmu_attach().
*
* This function is used when the MMU device is rebooted/reset, and the kernel
* wants to resume the state before it being turned off.
*
* Returns 0 on success, or -errno on error.
*/
int edgetpu_mmu_reattach(struct edgetpu_dev *dev);
void edgetpu_mmu_reset(struct edgetpu_dev *dev);
int edgetpu_mmu_map(struct edgetpu_dev *dev, struct edgetpu_mapping *map,
enum edgetpu_context_id context_id, u32 mmu_flags);
void edgetpu_mmu_unmap(struct edgetpu_dev *dev, struct edgetpu_mapping *map,
enum edgetpu_context_id context_id);
/**
* Maps TPU IOVA @iova to @sgt.
* @sgt: the sg table presents the list of pages.
*
* Description: Request TPU to map @iova to the pages presented by @sgt.
*
* Returns 0 on success, -errno on error.
*
* Note: Caller should use edgetpu_mmu_reserve() before calling this method if
* the target @iova isn't acquired from edgetpu_mmu_alloc(@etdev).
*/
int edgetpu_mmu_map_iova_sgt(struct edgetpu_dev *etdev, tpu_addr_t iova,
struct sg_table *sgt, enum dma_data_direction dir,
u32 mmu_flags, enum edgetpu_context_id context_id);
void edgetpu_mmu_unmap_iova_sgt_attrs(struct edgetpu_dev *etdev,
tpu_addr_t iova, struct sg_table *sgt,
enum dma_data_direction dir,
enum edgetpu_context_id context_id,
unsigned long attrs);
#define edgetpu_mmu_unmap_iova_sgt(e, i, s, d, c) \
edgetpu_mmu_unmap_iova_sgt_attrs(e, i, s, d, c, 0)
/**
* Allocates an IOVA in the internal MMU.
* @size: size needed to be allocated in bytes.
*
* Description: Allocates a TPU address to be mapped via
* edgetpu_mmu_add_translation().
*
* If the chip doesn't have an internal MMU then return zero.
*
* Returns zero on error.
*/
tpu_addr_t edgetpu_mmu_alloc(struct edgetpu_dev *etdev, size_t size,
u32 mmu_flags);
/**
* Marks the IOVA region [@tpu_addr, @tpu_addr + @size) as reserved.
*
* Description: Use this function to mark the region as reserved and prevents
* it from being allocated by edgetpu_mmu_alloc().
*
* Use edgetpu_mmu_free() to release the reserved area.
*/
void edgetpu_mmu_reserve(struct edgetpu_dev *etdev, tpu_addr_t tpu_addr,
size_t size);
/*
* Free the IOVA allocated by edgetpu_mmu_alloc() or reserved by
* edgetpu_mmu_reserve().
*/
void edgetpu_mmu_free(struct edgetpu_dev *etdev, tpu_addr_t tpu_addr,
size_t size);
/**
* Add an IOVA translation to the chip MMU/IOMMU.
* @iova: I/O virtual address (TPU VA) to map to paddr.
* @paddr: Physical/next-stage target address to which iova is to be mapped.
* @size: size of the mapping in bytes.
* @prot: IOMMU API protections to use for the mapping.
* @context_id: generic context ID for the mapping.
*
* Description: Add a mapping from iova -> paddr to the MMU for the chip.
* paddr can be considered a physical address from the TPU's viewpoint, but
* may actually be another IOVA for another IOMMU downstream of the chip MMU.
*
* Note: for chipsets with edgetpu_mmu_alloc() support, @iova passed to this
* function must be either allocated from edgetpu_mmu_alloc() or reserved by
* edgetpu_mmu_reserve().
*
* For chipsets with IOMMU AUX domain support, @context_id can be used to
* specify a detached IOMMU domain by value
* (EDGETPU_CONTEXT_DOMAIN_TOKEN | @token), where @token is the one returned by
* edgetpu_mmu_alloc_domain(). This description holds for all APIs in this file
* with @context_id as a parameter.
*/
int edgetpu_mmu_add_translation(struct edgetpu_dev *etdev, unsigned long iova,
phys_addr_t paddr, size_t size, int prot,
enum edgetpu_context_id context_id);
/* Remove a translation added by edgetpu_mmu_add_translation. */
void edgetpu_mmu_remove_translation(struct edgetpu_dev *etdev,
unsigned long iova, size_t size,
enum edgetpu_context_id context_id);
/**
* Add a TPU mapping for a local DMA mapping
* @down_addr: DMA (or physical) addr of memory downstream from TPU
* @size: size of memory area in bytes
* @dir: DMA direction of mapping
* @context_id: context ID for the mapping
* @mmu_flags: the flag or'ed with EDGETPU_MMU_* macros
*
* Description: For chips with internal MMUs, add the required internal MMU
* mapping for the TPU to access @down_addr, the DMA or physical address of the
* buffer as returned by the Linux DMA API when the DMA mapping was created.
* This can be used with, for example, buffers allocated using
* dma_alloc_coherent(), which are mapped appropriately for any downstream IOMMU
* and must be mapped to the TPU internal MMU as well.
*
* For a chip that doesn't have an internal MMU but has the IOMMU domain AUX
* feature, perform the necessary mapping to @context_id and return the
* downstream DMA address.
*
* Returns zero on error.
*/
tpu_addr_t edgetpu_mmu_tpu_map(struct edgetpu_dev *etdev, dma_addr_t down_addr,
size_t size, enum dma_data_direction dir,
enum edgetpu_context_id context_id,
u32 mmu_flags);
/* Unmap a TPU mapping created by edgetpu_mmu_tpu_map */
void edgetpu_mmu_tpu_unmap(struct edgetpu_dev *etdev,
tpu_addr_t tpu_addr, size_t size,
enum edgetpu_context_id context_id);
/**
* Add a TPU mapping towards an SG table.
* @sgt: An SG table that is already mapped to @etdev->dev, i.e. dma_map_sg*
* has been called.
* @dir: DMA direction of mapping
* @context_id: context ID for the mapping
* @mmu_flags: the flag or'ed with EDGETPU_MMU_* macros
*
* Description: For chips with internal MMUs, add the required internal MMU
* mapping for the TPU to access the DMA addresses of @sgt.
*
* For a chip that doesn't have an internal MMU but has the IOMMU domain AUX
* feature, perform the necessary mapping to @context_id and return the
* downstream DMA address.
*
* Caller ensures the SG table has DMA addresses as compact as possible, that is
* if @sgt->nents is greater than 1 then the DMA addresses are not continuous.
*
* Returns zero on error.
*/
tpu_addr_t edgetpu_mmu_tpu_map_sgt(struct edgetpu_dev *etdev,
struct sg_table *sgt,
enum dma_data_direction dir,
enum edgetpu_context_id context_id,
u32 mmu_flags);
/* Unmap a TPU mapping created by edgetpu_mmu_tpu_map_sgt */
void edgetpu_mmu_tpu_unmap_sgt(struct edgetpu_dev *etdev, tpu_addr_t tpu_addr,
struct sg_table *sgt,
enum edgetpu_context_id context_id);
/*
* Hints the MMU to use edgetpu_device_dram_alloc() for allocating MMU page
* tables.
*
* @use_dev_dram: true = use device DRAM for MMU data structures
* false = use host DRAM for MMU data structures
*/
void edgetpu_mmu_use_dev_dram(struct edgetpu_dev *etdev, bool use_dev_dram);
/*
* Allocates a IOMMU domain.
*
* The newly allocated domain would have @pasid equal IOMMU_PASID_INVALID, use
* edgetpu_mmu_attach_domain() to acquire a valid PASID.
*
* If the chipset doesn't need to drive the domain AUX feature, a valid
* pointer shall be returned with @etdomain->pasid == IOMMU_PASID_INVALID.
*
* Returns NULL on error.
*/
struct edgetpu_iommu_domain *
edgetpu_mmu_alloc_domain(struct edgetpu_dev *etdev);
/* Frees the domain previously allocated by edgetpu_mmu_alloc_domain(). */
void edgetpu_mmu_free_domain(struct edgetpu_dev *etdev,
struct edgetpu_iommu_domain *etdomain);
/*
* Attaches the domain to the MMU device.
*
* If the chipset doesn't need to drive the domain AUX feature, this function
* should return 0 without setting @etdomain->pasid.
*
* When success, 0 is returned and @etdomain->pasid is set.
* Returns -errno on error.
*/
int edgetpu_mmu_attach_domain(struct edgetpu_dev *etdev,
struct edgetpu_iommu_domain *etdomain);
/* Detaches the domain from the MMU device. */
void edgetpu_mmu_detach_domain(struct edgetpu_dev *etdev,
struct edgetpu_iommu_domain *etdomain);
#endif /* __EDGETPU_MMU_H__ */