188bc9d19SHeikki Krogerus /** 288bc9d19SHeikki Krogerus * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface 388bc9d19SHeikki Krogerus * 488bc9d19SHeikki Krogerus * Copyright (C) 2015 Intel Corporation 588bc9d19SHeikki Krogerus * 688bc9d19SHeikki Krogerus * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 788bc9d19SHeikki Krogerus * 888bc9d19SHeikki Krogerus * This program is free software; you can redistribute it and/or modify 988bc9d19SHeikki Krogerus * it under the terms of the GNU General Public License version 2 as 1088bc9d19SHeikki Krogerus * published by the Free Software Foundation. 1188bc9d19SHeikki Krogerus */ 1288bc9d19SHeikki Krogerus 1388bc9d19SHeikki Krogerus #include <linux/ulpi/regs.h> 1488bc9d19SHeikki Krogerus 1588bc9d19SHeikki Krogerus #include "core.h" 1688bc9d19SHeikki Krogerus #include "io.h" 1788bc9d19SHeikki Krogerus 1888bc9d19SHeikki Krogerus #define DWC3_ULPI_ADDR(a) \ 1988bc9d19SHeikki Krogerus ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ 2088bc9d19SHeikki Krogerus DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ 2188bc9d19SHeikki Krogerus DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a)) 2288bc9d19SHeikki Krogerus 2388bc9d19SHeikki Krogerus static int dwc3_ulpi_busyloop(struct dwc3 *dwc) 2488bc9d19SHeikki Krogerus { 2588bc9d19SHeikki Krogerus unsigned count = 1000; 2688bc9d19SHeikki Krogerus u32 reg; 2788bc9d19SHeikki Krogerus 2888bc9d19SHeikki Krogerus while (count--) { 2988bc9d19SHeikki Krogerus reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 3088bc9d19SHeikki Krogerus if (!(reg & DWC3_GUSB2PHYACC_BUSY)) 3188bc9d19SHeikki Krogerus return 0; 3288bc9d19SHeikki Krogerus cpu_relax(); 3388bc9d19SHeikki Krogerus } 3488bc9d19SHeikki Krogerus 3588bc9d19SHeikki Krogerus return -ETIMEDOUT; 3688bc9d19SHeikki Krogerus } 3788bc9d19SHeikki Krogerus 38b7cf1dc3STal Shorer static int dwc3_ulpi_read(struct device *dev, u8 addr) 3988bc9d19SHeikki Krogerus { 40b7cf1dc3STal Shorer struct dwc3 *dwc = dev_get_drvdata(dev); 4188bc9d19SHeikki Krogerus u32 reg; 4288bc9d19SHeikki Krogerus int ret; 4388bc9d19SHeikki Krogerus 4488bc9d19SHeikki Krogerus reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 4588bc9d19SHeikki Krogerus dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 4688bc9d19SHeikki Krogerus 4788bc9d19SHeikki Krogerus ret = dwc3_ulpi_busyloop(dwc); 4888bc9d19SHeikki Krogerus if (ret) 4988bc9d19SHeikki Krogerus return ret; 5088bc9d19SHeikki Krogerus 5188bc9d19SHeikki Krogerus reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 5288bc9d19SHeikki Krogerus 5388bc9d19SHeikki Krogerus return DWC3_GUSB2PHYACC_DATA(reg); 5488bc9d19SHeikki Krogerus } 5588bc9d19SHeikki Krogerus 56b7cf1dc3STal Shorer static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val) 5788bc9d19SHeikki Krogerus { 58b7cf1dc3STal Shorer struct dwc3 *dwc = dev_get_drvdata(dev); 5988bc9d19SHeikki Krogerus u32 reg; 6088bc9d19SHeikki Krogerus 6188bc9d19SHeikki Krogerus reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 6288bc9d19SHeikki Krogerus reg |= DWC3_GUSB2PHYACC_WRITE | val; 6388bc9d19SHeikki Krogerus dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 6488bc9d19SHeikki Krogerus 6588bc9d19SHeikki Krogerus return dwc3_ulpi_busyloop(dwc); 6688bc9d19SHeikki Krogerus } 6788bc9d19SHeikki Krogerus 6888bc9d19SHeikki Krogerus static struct ulpi_ops dwc3_ulpi_ops = { 69b7cf1dc3STal Shorer .read_dev = dwc3_ulpi_read, 70b7cf1dc3STal Shorer .write_dev = dwc3_ulpi_write, 7188bc9d19SHeikki Krogerus }; 7288bc9d19SHeikki Krogerus 7388bc9d19SHeikki Krogerus int dwc3_ulpi_init(struct dwc3 *dwc) 7488bc9d19SHeikki Krogerus { 7588bc9d19SHeikki Krogerus /* Register the interface */ 7688bc9d19SHeikki Krogerus dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops); 7788bc9d19SHeikki Krogerus if (IS_ERR(dwc->ulpi)) { 7888bc9d19SHeikki Krogerus dev_err(dwc->dev, "failed to register ULPI interface"); 7988bc9d19SHeikki Krogerus return PTR_ERR(dwc->ulpi); 8088bc9d19SHeikki Krogerus } 8188bc9d19SHeikki Krogerus 8288bc9d19SHeikki Krogerus return 0; 8388bc9d19SHeikki Krogerus } 8488bc9d19SHeikki Krogerus 8588bc9d19SHeikki Krogerus void dwc3_ulpi_exit(struct dwc3 *dwc) 8688bc9d19SHeikki Krogerus { 8788bc9d19SHeikki Krogerus if (dwc->ulpi) { 8888bc9d19SHeikki Krogerus ulpi_unregister_interface(dwc->ulpi); 8988bc9d19SHeikki Krogerus dwc->ulpi = NULL; 9088bc9d19SHeikki Krogerus } 9188bc9d19SHeikki Krogerus } 92