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