From dd6b68ed4f5bba78c0cd9765fa48b10cb1542dc7 Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Sat, 21 Jan 2023 20:27:52 +0100 Subject: iommu: Add DMA mapping operations In order to support IOMMUs in non-bypass mode we need device ops to map and unmap DMA memory. The map operation enters a mapping for a region specified by CPU address and size into the translation table of the IOMMU and returns a DMA address suitable for programming the device to do DMA. The unmap operation removes this mapping from the translation table of the IOMMU. Signed-off-by: Mark Kettenis --- include/dm/device.h | 4 ++++ include/iommu.h | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'include') diff --git a/include/dm/device.h b/include/dm/device.h index f3f953c9afc..e9460386ca9 100644 --- a/include/dm/device.h +++ b/include/dm/device.h @@ -165,6 +165,7 @@ enum { * automatically when the device is removed / unbound * @dma_offset: Offset between the physical address space (CPU's) and the * device's bus address space + * @iommu: IOMMU device associated with this device */ struct udevice { const struct driver *driver; @@ -194,6 +195,9 @@ struct udevice { #if CONFIG_IS_ENABLED(DM_DMA) ulong dma_offset; #endif +#if CONFIG_IS_ENABLED(IOMMU) + struct udevice *iommu; +#endif }; static inline int dm_udevice_size(void) diff --git a/include/iommu.h b/include/iommu.h index 6c46adf449e..cf9719c5e91 100644 --- a/include/iommu.h +++ b/include/iommu.h @@ -3,6 +3,27 @@ struct udevice; +struct iommu_ops { + /** + * map() - map DMA memory + * + * @dev: device for which to map DMA memory + * @addr: CPU address of the memory + * @size: size of the memory + * @return DMA address for the device + */ + dma_addr_t (*map)(struct udevice *dev, void *addr, size_t size); + + /** + * unmap() - unmap DMA memory + * + * @dev: device for which to unmap DMA memory + * @addr: DMA address of the memory + * @size: size of the memory + */ + void (*unmap)(struct udevice *dev, dma_addr_t addr, size_t size); +}; + #if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) && \ CONFIG_IS_ENABLED(IOMMU) int dev_iommu_enable(struct udevice *dev); @@ -13,4 +34,7 @@ static inline int dev_iommu_enable(struct udevice *dev) } #endif +dma_addr_t dev_iommu_dma_map(struct udevice *dev, void *addr, size_t size); +void dev_iommu_dma_unmap(struct udevice *dev, dma_addr_t addr, size_t size); + #endif -- cgit v1.2.3 From ba1efb3d2494e8ceb0ff405642f44a955f8d8e1b Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Sat, 21 Jan 2023 20:27:55 +0100 Subject: 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 Reviewed-by: Marek Vasut --- include/usb/xhci.h | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/usb/xhci.h b/include/usb/xhci.h index ea4cf3f52ba..85c359fa1b9 100644 --- a/include/usb/xhci.h +++ b/include/usb/xhci.h @@ -16,6 +16,7 @@ #ifndef HOST_XHCI_H_ #define HOST_XHCI_H_ +#include #include #include #include @@ -490,6 +491,7 @@ struct xhci_container_ctx { int size; u8 *bytes; + dma_addr_t dma; }; /** @@ -688,6 +690,8 @@ struct xhci_input_control_ctx { 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 @@ struct xhci_segment { 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_entry { 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_ctrl { 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 @@ struct xhci_ctrl { #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 @@ void xhci_slot_copy(struct xhci_ctrl *ctrl, 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 @@ extern struct dm_usb_ops xhci_usb_ops; 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_ */ -- cgit v1.2.3 From e330c8b83e8784d23614f80ca3f12b11ceb515d8 Mon Sep 17 00:00:00 2001 From: Mark Kettenis Date: Sat, 21 Jan 2023 20:28:00 +0100 Subject: usb: xhci: Fix root hub descriptor When a system has multiple XHCI controllers, some of the properties described in the descriptor of the root hub (such as the number of ports) might differ between controllers. Fix this by switching from a single global hub descriptor to a hub descriptor per controller. Signed-off-by: Mark Kettenis Reviewed-by: Marek Vasut --- include/usb/xhci.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/usb/xhci.h b/include/usb/xhci.h index 85c359fa1b9..4a4ac10229a 100644 --- a/include/usb/xhci.h +++ b/include/usb/xhci.h @@ -1222,6 +1222,7 @@ struct xhci_ctrl { struct xhci_erst_entry entry[ERST_NUM_SEGS]; struct xhci_scratchpad *scratchpad; struct xhci_virt_device *devs[MAX_HC_SLOTS]; + struct usb_hub_descriptor hub_desc; int rootdev; u16 hci_version; int page_size; -- cgit v1.2.3