usb: xhci: Implement DMA mapping
An XHCI controller that sits behind an IOMMU needs to map and unmap
its memory buffers to do DMA. Implement this by inroducing new
xhci_dma_map() and xhci_dma_unmap() helper functions. The
xhci_dma_map() function replaces the existing xhci_virt_to_bus()
function in the sense that it returns the bus address in the case
of simple address translation in the absence of an IOMMU. The
xhci_bus_to_virt() function is eliminated by storing the CPU
address of the allocated scratchpad memory in struct xhci_ctrl.
Signed-off-by: Mark Kettenis <kettenis@openbsd.org>
Reviewed-by: Marek Vasut <marex@denx.de>
diff --git a/include/usb/xhci.h b/include/usb/xhci.h
index ea4cf3f..85c359f 100644
--- a/include/usb/xhci.h
+++ b/include/usb/xhci.h
@@ -16,6 +16,7 @@
#ifndef HOST_XHCI_H_
#define HOST_XHCI_H_
+#include <iommu.h>
#include <phys2bus.h>
#include <asm/types.h>
#include <asm/cache.h>
@@ -490,6 +491,7 @@
int size;
u8 *bytes;
+ dma_addr_t dma;
};
/**
@@ -688,6 +690,8 @@
struct xhci_device_context_array {
/* 64-bit device addresses; we only write 32-bit addresses */
__le64 dev_context_ptrs[MAX_HC_SLOTS];
+ /* private xHCD pointers */
+ dma_addr_t dma;
};
/* TODO: write function to set the 64-bit device DMA address */
/*
@@ -997,6 +1001,7 @@
union xhci_trb *trbs;
/* private to HCD */
struct xhci_segment *next;
+ dma_addr_t dma;
};
struct xhci_ring {
@@ -1025,11 +1030,14 @@
struct xhci_erst {
struct xhci_erst_entry *entries;
unsigned int num_entries;
+ /* xhci->event_ring keeps track of segment dma addresses */
+ dma_addr_t erst_dma_addr;
/* Num entries the ERST can contain */
unsigned int erst_size;
};
struct xhci_scratchpad {
+ void *scratchpad;
u64 *sp_array;
};
@@ -1216,6 +1224,7 @@
struct xhci_virt_device *devs[MAX_HC_SLOTS];
int rootdev;
u16 hci_version;
+ int page_size;
u32 quirks;
#define XHCI_MTK_HOST BIT(0)
};
@@ -1226,7 +1235,7 @@
#define xhci_to_dev(_ctrl) NULL
#endif
-unsigned long trb_addr(struct xhci_segment *seg, union xhci_trb *trb);
+dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
struct xhci_input_control_ctx
*xhci_get_input_control_ctx(struct xhci_container_ctx *ctx);
struct xhci_slot_ctx *xhci_get_slot_ctx(struct xhci_ctrl *ctrl,
@@ -1243,7 +1252,7 @@
struct xhci_container_ctx *out_ctx);
void xhci_setup_addressable_virt_dev(struct xhci_ctrl *ctrl,
struct usb_device *udev, int hop_portnr);
-void xhci_queue_command(struct xhci_ctrl *ctrl, u8 *ptr,
+void xhci_queue_command(struct xhci_ctrl *ctrl, dma_addr_t addr,
u32 slot_id, u32 ep_index, trb_type cmd);
void xhci_acknowledge_event(struct xhci_ctrl *ctrl);
union xhci_trb *xhci_wait_for_event(struct xhci_ctrl *ctrl, trb_type expected);
@@ -1284,14 +1293,22 @@
struct xhci_ctrl *xhci_get_ctrl(struct usb_device *udev);
-static inline dma_addr_t xhci_virt_to_bus(struct xhci_ctrl *ctrl, void *addr)
+static inline dma_addr_t xhci_dma_map(struct xhci_ctrl *ctrl, void *addr,
+ size_t size)
{
+#if CONFIG_IS_ENABLED(IOMMU)
+ return dev_iommu_dma_map(xhci_to_dev(ctrl), addr, size);
+#else
return dev_phys_to_bus(xhci_to_dev(ctrl), virt_to_phys(addr));
+#endif
}
-static inline void *xhci_bus_to_virt(struct xhci_ctrl *ctrl, dma_addr_t addr)
+static inline void xhci_dma_unmap(struct xhci_ctrl *ctrl, dma_addr_t addr,
+ size_t size)
{
- return phys_to_virt(dev_bus_to_phys(xhci_to_dev(ctrl), addr));
+#if CONFIG_IS_ENABLED(IOMMU)
+ dev_iommu_dma_unmap(xhci_to_dev(ctrl), addr, size);
+#endif
}
#endif /* HOST_XHCI_H_ */