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 10*fca3f138SSerge Semin #include <linux/delay.h> 11*fca3f138SSerge Semin #include <linux/time64.h> 1288bc9d19SHeikki Krogerus #include <linux/ulpi/regs.h> 1388bc9d19SHeikki Krogerus 1488bc9d19SHeikki Krogerus #include "core.h" 1588bc9d19SHeikki Krogerus #include "io.h" 1688bc9d19SHeikki Krogerus 1788bc9d19SHeikki Krogerus #define DWC3_ULPI_ADDR(a) \ 1888bc9d19SHeikki Krogerus ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ 1988bc9d19SHeikki Krogerus DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ 2088bc9d19SHeikki Krogerus DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a)) 2188bc9d19SHeikki Krogerus 22*fca3f138SSerge Semin #define DWC3_ULPI_BASE_DELAY DIV_ROUND_UP(NSEC_PER_SEC, 60000000L) 23*fca3f138SSerge Semin 24*fca3f138SSerge Semin static int dwc3_ulpi_busyloop(struct dwc3 *dwc, u8 addr, bool read) 2588bc9d19SHeikki Krogerus { 26*fca3f138SSerge Semin unsigned long ns = 5L * DWC3_ULPI_BASE_DELAY; 272a499b45SFelipe Balbi unsigned int count = 1000; 2888bc9d19SHeikki Krogerus u32 reg; 2988bc9d19SHeikki Krogerus 30*fca3f138SSerge Semin if (addr >= ULPI_EXT_VENDOR_SPECIFIC) 31*fca3f138SSerge Semin ns += DWC3_ULPI_BASE_DELAY; 32*fca3f138SSerge Semin 33*fca3f138SSerge Semin if (read) 34*fca3f138SSerge Semin ns += DWC3_ULPI_BASE_DELAY; 35*fca3f138SSerge Semin 3688bc9d19SHeikki Krogerus while (count--) { 37*fca3f138SSerge Semin ndelay(ns); 3888bc9d19SHeikki Krogerus reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 39ce722da6SSerge Semin if (reg & DWC3_GUSB2PHYACC_DONE) 4088bc9d19SHeikki Krogerus return 0; 4188bc9d19SHeikki Krogerus cpu_relax(); 4288bc9d19SHeikki Krogerus } 4388bc9d19SHeikki Krogerus 4488bc9d19SHeikki Krogerus return -ETIMEDOUT; 4588bc9d19SHeikki Krogerus } 4688bc9d19SHeikki Krogerus 47b7cf1dc3STal Shorer static int dwc3_ulpi_read(struct device *dev, u8 addr) 4888bc9d19SHeikki Krogerus { 49b7cf1dc3STal Shorer struct dwc3 *dwc = dev_get_drvdata(dev); 5088bc9d19SHeikki Krogerus u32 reg; 5188bc9d19SHeikki Krogerus int ret; 5288bc9d19SHeikki Krogerus 53e0082698SFelipe Balbi reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 54e0082698SFelipe Balbi if (reg & DWC3_GUSB2PHYCFG_SUSPHY) { 55e0082698SFelipe Balbi reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; 56e0082698SFelipe Balbi dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 57e0082698SFelipe Balbi } 58e0082698SFelipe Balbi 5988bc9d19SHeikki Krogerus reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 6088bc9d19SHeikki Krogerus dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 6188bc9d19SHeikki Krogerus 62*fca3f138SSerge Semin ret = dwc3_ulpi_busyloop(dwc, addr, true); 6388bc9d19SHeikki Krogerus if (ret) 6488bc9d19SHeikki Krogerus return ret; 6588bc9d19SHeikki Krogerus 6688bc9d19SHeikki Krogerus reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 6788bc9d19SHeikki Krogerus 6888bc9d19SHeikki Krogerus return DWC3_GUSB2PHYACC_DATA(reg); 6988bc9d19SHeikki Krogerus } 7088bc9d19SHeikki Krogerus 71b7cf1dc3STal Shorer static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val) 7288bc9d19SHeikki Krogerus { 73b7cf1dc3STal Shorer struct dwc3 *dwc = dev_get_drvdata(dev); 7488bc9d19SHeikki Krogerus u32 reg; 7588bc9d19SHeikki Krogerus 76e0082698SFelipe Balbi reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 77e0082698SFelipe Balbi if (reg & DWC3_GUSB2PHYCFG_SUSPHY) { 78e0082698SFelipe Balbi reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; 79e0082698SFelipe Balbi dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 80e0082698SFelipe Balbi } 81e0082698SFelipe Balbi 8288bc9d19SHeikki Krogerus reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 8388bc9d19SHeikki Krogerus reg |= DWC3_GUSB2PHYACC_WRITE | val; 8488bc9d19SHeikki Krogerus dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 8588bc9d19SHeikki Krogerus 86*fca3f138SSerge Semin return dwc3_ulpi_busyloop(dwc, addr, false); 8788bc9d19SHeikki Krogerus } 8888bc9d19SHeikki Krogerus 89073c47acSTal Shorer static const struct ulpi_ops dwc3_ulpi_ops = { 90e6f74849STal Shorer .read = dwc3_ulpi_read, 91e6f74849STal Shorer .write = dwc3_ulpi_write, 9288bc9d19SHeikki Krogerus }; 9388bc9d19SHeikki Krogerus 9488bc9d19SHeikki Krogerus int dwc3_ulpi_init(struct dwc3 *dwc) 9588bc9d19SHeikki Krogerus { 9688bc9d19SHeikki Krogerus /* Register the interface */ 9788bc9d19SHeikki Krogerus dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops); 9888bc9d19SHeikki Krogerus if (IS_ERR(dwc->ulpi)) { 9988bc9d19SHeikki Krogerus dev_err(dwc->dev, "failed to register ULPI interface"); 10088bc9d19SHeikki Krogerus return PTR_ERR(dwc->ulpi); 10188bc9d19SHeikki Krogerus } 10288bc9d19SHeikki Krogerus 10388bc9d19SHeikki Krogerus return 0; 10488bc9d19SHeikki Krogerus } 10588bc9d19SHeikki Krogerus 10688bc9d19SHeikki Krogerus void dwc3_ulpi_exit(struct dwc3 *dwc) 10788bc9d19SHeikki Krogerus { 10888bc9d19SHeikki Krogerus if (dwc->ulpi) { 10988bc9d19SHeikki Krogerus ulpi_unregister_interface(dwc->ulpi); 11088bc9d19SHeikki Krogerus dwc->ulpi = NULL; 11188bc9d19SHeikki Krogerus } 11288bc9d19SHeikki Krogerus } 113