15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2fb678a5aSLee Jones /* 388bc9d19SHeikki Krogerus * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface 488bc9d19SHeikki Krogerus * 588bc9d19SHeikki Krogerus * Copyright (C) 2015 Intel Corporation 688bc9d19SHeikki Krogerus * 788bc9d19SHeikki Krogerus * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> 888bc9d19SHeikki Krogerus */ 988bc9d19SHeikki Krogerus 1088bc9d19SHeikki Krogerus #include <linux/ulpi/regs.h> 1188bc9d19SHeikki Krogerus 1288bc9d19SHeikki Krogerus #include "core.h" 1388bc9d19SHeikki Krogerus #include "io.h" 1488bc9d19SHeikki Krogerus 1588bc9d19SHeikki Krogerus #define DWC3_ULPI_ADDR(a) \ 1688bc9d19SHeikki Krogerus ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ 1788bc9d19SHeikki Krogerus DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ 1888bc9d19SHeikki Krogerus DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a)) 1988bc9d19SHeikki Krogerus 2088bc9d19SHeikki Krogerus static int dwc3_ulpi_busyloop(struct dwc3 *dwc) 2188bc9d19SHeikki Krogerus { 222a499b45SFelipe Balbi unsigned int count = 1000; 2388bc9d19SHeikki Krogerus u32 reg; 2488bc9d19SHeikki Krogerus 2588bc9d19SHeikki Krogerus while (count--) { 2688bc9d19SHeikki Krogerus reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 27*ce722da6SSerge Semin if (reg & DWC3_GUSB2PHYACC_DONE) 2888bc9d19SHeikki Krogerus return 0; 2988bc9d19SHeikki Krogerus cpu_relax(); 3088bc9d19SHeikki Krogerus } 3188bc9d19SHeikki Krogerus 3288bc9d19SHeikki Krogerus return -ETIMEDOUT; 3388bc9d19SHeikki Krogerus } 3488bc9d19SHeikki Krogerus 35b7cf1dc3STal Shorer static int dwc3_ulpi_read(struct device *dev, u8 addr) 3688bc9d19SHeikki Krogerus { 37b7cf1dc3STal Shorer struct dwc3 *dwc = dev_get_drvdata(dev); 3888bc9d19SHeikki Krogerus u32 reg; 3988bc9d19SHeikki Krogerus int ret; 4088bc9d19SHeikki Krogerus 41e0082698SFelipe Balbi reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 42e0082698SFelipe Balbi if (reg & DWC3_GUSB2PHYCFG_SUSPHY) { 43e0082698SFelipe Balbi reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; 44e0082698SFelipe Balbi dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 45e0082698SFelipe Balbi } 46e0082698SFelipe Balbi 4788bc9d19SHeikki Krogerus reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 4888bc9d19SHeikki Krogerus dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 4988bc9d19SHeikki Krogerus 5088bc9d19SHeikki Krogerus ret = dwc3_ulpi_busyloop(dwc); 5188bc9d19SHeikki Krogerus if (ret) 5288bc9d19SHeikki Krogerus return ret; 5388bc9d19SHeikki Krogerus 5488bc9d19SHeikki Krogerus reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 5588bc9d19SHeikki Krogerus 5688bc9d19SHeikki Krogerus return DWC3_GUSB2PHYACC_DATA(reg); 5788bc9d19SHeikki Krogerus } 5888bc9d19SHeikki Krogerus 59b7cf1dc3STal Shorer static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val) 6088bc9d19SHeikki Krogerus { 61b7cf1dc3STal Shorer struct dwc3 *dwc = dev_get_drvdata(dev); 6288bc9d19SHeikki Krogerus u32 reg; 6388bc9d19SHeikki Krogerus 64e0082698SFelipe Balbi reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 65e0082698SFelipe Balbi if (reg & DWC3_GUSB2PHYCFG_SUSPHY) { 66e0082698SFelipe Balbi reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; 67e0082698SFelipe Balbi dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 68e0082698SFelipe Balbi } 69e0082698SFelipe Balbi 7088bc9d19SHeikki Krogerus reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 7188bc9d19SHeikki Krogerus reg |= DWC3_GUSB2PHYACC_WRITE | val; 7288bc9d19SHeikki Krogerus dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 7388bc9d19SHeikki Krogerus 7488bc9d19SHeikki Krogerus return dwc3_ulpi_busyloop(dwc); 7588bc9d19SHeikki Krogerus } 7688bc9d19SHeikki Krogerus 77073c47acSTal Shorer static const struct ulpi_ops dwc3_ulpi_ops = { 78e6f74849STal Shorer .read = dwc3_ulpi_read, 79e6f74849STal Shorer .write = dwc3_ulpi_write, 8088bc9d19SHeikki Krogerus }; 8188bc9d19SHeikki Krogerus 8288bc9d19SHeikki Krogerus int dwc3_ulpi_init(struct dwc3 *dwc) 8388bc9d19SHeikki Krogerus { 8488bc9d19SHeikki Krogerus /* Register the interface */ 8588bc9d19SHeikki Krogerus dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops); 8688bc9d19SHeikki Krogerus if (IS_ERR(dwc->ulpi)) { 8788bc9d19SHeikki Krogerus dev_err(dwc->dev, "failed to register ULPI interface"); 8888bc9d19SHeikki Krogerus return PTR_ERR(dwc->ulpi); 8988bc9d19SHeikki Krogerus } 9088bc9d19SHeikki Krogerus 9188bc9d19SHeikki Krogerus return 0; 9288bc9d19SHeikki Krogerus } 9388bc9d19SHeikki Krogerus 9488bc9d19SHeikki Krogerus void dwc3_ulpi_exit(struct dwc3 *dwc) 9588bc9d19SHeikki Krogerus { 9688bc9d19SHeikki Krogerus if (dwc->ulpi) { 9788bc9d19SHeikki Krogerus ulpi_unregister_interface(dwc->ulpi); 9888bc9d19SHeikki Krogerus dwc->ulpi = NULL; 9988bc9d19SHeikki Krogerus } 10088bc9d19SHeikki Krogerus } 101