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