ulpi.c (2331B)
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/delay.h> 11#include <linux/time64.h> 12#include <linux/ulpi/regs.h> 13 14#include "core.h" 15#include "io.h" 16 17#define DWC3_ULPI_ADDR(a) \ 18 ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ 19 DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ 20 DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a)) 21 22#define DWC3_ULPI_BASE_DELAY DIV_ROUND_UP(NSEC_PER_SEC, 60000000L) 23 24static int dwc3_ulpi_busyloop(struct dwc3 *dwc, u8 addr, bool read) 25{ 26 unsigned long ns = 5L * DWC3_ULPI_BASE_DELAY; 27 unsigned int count = 10000; 28 u32 reg; 29 30 if (addr >= ULPI_EXT_VENDOR_SPECIFIC) 31 ns += DWC3_ULPI_BASE_DELAY; 32 33 if (read) 34 ns += DWC3_ULPI_BASE_DELAY; 35 36 reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 37 if (reg & DWC3_GUSB2PHYCFG_SUSPHY) 38 usleep_range(1000, 1200); 39 40 while (count--) { 41 ndelay(ns); 42 reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 43 if (reg & DWC3_GUSB2PHYACC_DONE) 44 return 0; 45 cpu_relax(); 46 } 47 48 return -ETIMEDOUT; 49} 50 51static int dwc3_ulpi_read(struct device *dev, u8 addr) 52{ 53 struct dwc3 *dwc = dev_get_drvdata(dev); 54 u32 reg; 55 int ret; 56 57 reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 58 dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 59 60 ret = dwc3_ulpi_busyloop(dwc, addr, true); 61 if (ret) 62 return ret; 63 64 reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); 65 66 return DWC3_GUSB2PHYACC_DATA(reg); 67} 68 69static int dwc3_ulpi_write(struct device *dev, u8 addr, u8 val) 70{ 71 struct dwc3 *dwc = dev_get_drvdata(dev); 72 u32 reg; 73 74 reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); 75 reg |= DWC3_GUSB2PHYACC_WRITE | val; 76 dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); 77 78 return dwc3_ulpi_busyloop(dwc, addr, false); 79} 80 81static const struct ulpi_ops dwc3_ulpi_ops = { 82 .read = dwc3_ulpi_read, 83 .write = dwc3_ulpi_write, 84}; 85 86int dwc3_ulpi_init(struct dwc3 *dwc) 87{ 88 /* Register the interface */ 89 dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops); 90 if (IS_ERR(dwc->ulpi)) { 91 dev_err(dwc->dev, "failed to register ULPI interface"); 92 return PTR_ERR(dwc->ulpi); 93 } 94 95 return 0; 96} 97 98void dwc3_ulpi_exit(struct dwc3 *dwc) 99{ 100 if (dwc->ulpi) { 101 ulpi_unregister_interface(dwc->ulpi); 102 dwc->ulpi = NULL; 103 } 104}