xref: /openbmc/linux/drivers/usb/dwc3/ulpi.c (revision ce722da6)
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