1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface 4 * 5 * Copyright (C) 2015 Intel Corporation 6 * 7 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 8 */ 9 10 #include <linux/ulpi/regs.h> 11 12 #include "core.h" 13 #include "io.h" 14 15 #define DWC3_ULPI_ADDR(a) \ 16 ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ 17 DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ 18 DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a)) 19 20 static int dwc3_ulpi_busyloop(struct dwc3 *dwc) 21 { 22 unsigned count = 1000; 23 u32 reg; 24 25 while (count--) { 26 reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 27 if (!(reg & DWC3_GUSB2PHYACC_BUSY)) 28 return 0; 29 cpu_relax(); 30 } 31 32 return -ETIMEDOUT; 33 } 34 35 static int dwc3_ulpi_read(struct device *dev, u8 addr) 36 { 37 struct dwc3 *dwc = dev_get_drvdata(dev); 38 u32 reg; 39 int ret; 40 41 reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 42 if (reg & DWC3_GUSB2PHYCFG_SUSPHY) { 43 reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; 44 dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 45 } 46 47 reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 48 dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 49 50 ret = dwc3_ulpi_busyloop(dwc); 51 if (ret) 52 return ret; 53 54 reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 55 56 return DWC3_GUSB2PHYACC_DATA(reg); 57 } 58 59 static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val) 60 { 61 struct dwc3 *dwc = dev_get_drvdata(dev); 62 u32 reg; 63 64 reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 65 if (reg & DWC3_GUSB2PHYCFG_SUSPHY) { 66 reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; 67 dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 68 } 69 70 reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 71 reg |= DWC3_GUSB2PHYACC_WRITE | val; 72 dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 73 74 return dwc3_ulpi_busyloop(dwc); 75 } 76 77 static const struct ulpi_ops dwc3_ulpi_ops = { 78 .read = dwc3_ulpi_read, 79 .write = dwc3_ulpi_write, 80 }; 81 82 int dwc3_ulpi_init(struct dwc3 *dwc) 83 { 84 /* Register the interface */ 85 dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops); 86 if (IS_ERR(dwc->ulpi)) { 87 dev_err(dwc->dev, "failed to register ULPI interface"); 88 return PTR_ERR(dwc->ulpi); 89 } 90 91 return 0; 92 } 93 94 void dwc3_ulpi_exit(struct dwc3 *dwc) 95 { 96 if (dwc->ulpi) { 97 ulpi_unregister_interface(dwc->ulpi); 98 dwc->ulpi = NULL; 99 } 100 } 101