1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2014 Marvell 4 * Author: Gregory CLEMENT <gregory.clement@free-electrons.com> 5 */ 6 7 #include <linux/io.h> 8 #include <linux/mbus.h> 9 #include <linux/of.h> 10 #include <linux/platform_device.h> 11 12 #include <linux/usb.h> 13 #include <linux/usb/hcd.h> 14 15 #include "xhci-mvebu.h" 16 #include "xhci.h" 17 18 #define USB3_MAX_WINDOWS 4 19 #define USB3_WIN_CTRL(w) (0x0 + ((w) * 8)) 20 #define USB3_WIN_BASE(w) (0x4 + ((w) * 8)) 21 22 static void xhci_mvebu_mbus_config(void __iomem *base, 23 const struct mbus_dram_target_info *dram) 24 { 25 int win; 26 27 /* Clear all existing windows */ 28 for (win = 0; win < USB3_MAX_WINDOWS; win++) { 29 writel(0, base + USB3_WIN_CTRL(win)); 30 writel(0, base + USB3_WIN_BASE(win)); 31 } 32 33 /* Program each DRAM CS in a seperate window */ 34 for (win = 0; win < dram->num_cs; win++) { 35 const struct mbus_dram_window *cs = &dram->cs[win]; 36 37 writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) | 38 (dram->mbus_dram_target_id << 4) | 1, 39 base + USB3_WIN_CTRL(win)); 40 41 writel((cs->base & 0xffff0000), base + USB3_WIN_BASE(win)); 42 } 43 } 44 45 int xhci_mvebu_mbus_init_quirk(struct usb_hcd *hcd) 46 { 47 struct device *dev = hcd->self.controller; 48 struct platform_device *pdev = to_platform_device(dev); 49 struct resource *res; 50 void __iomem *base; 51 const struct mbus_dram_target_info *dram; 52 53 res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 54 if (!res) 55 return -ENODEV; 56 57 /* 58 * We don't use devm_ioremap() because this mapping should 59 * only exists for the duration of this probe function. 60 */ 61 base = ioremap(res->start, resource_size(res)); 62 if (!base) 63 return -ENODEV; 64 65 dram = mv_mbus_dram_info(); 66 xhci_mvebu_mbus_config(base, dram); 67 68 /* 69 * This memory area was only needed to configure the MBus 70 * windows, and is therefore no longer useful. 71 */ 72 iounmap(base); 73 74 return 0; 75 } 76 77 int xhci_mvebu_a3700_init_quirk(struct usb_hcd *hcd) 78 { 79 struct xhci_hcd *xhci = hcd_to_xhci(hcd); 80 81 /* Without reset on resume, the HC won't work at all */ 82 xhci->quirks |= XHCI_RESET_ON_RESUME; 83 84 return 0; 85 } 86