xref: /linux/drivers/usb/chipidea/ulpi.c (revision 66e72a01b60ae6950ddbb3585fdc1424d303e14b)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
27bb7e9b1SStephen Boyd /*
37bb7e9b1SStephen Boyd  * Copyright (c) 2016 Linaro Ltd.
47bb7e9b1SStephen Boyd  */
57bb7e9b1SStephen Boyd 
67bb7e9b1SStephen Boyd #include <linux/device.h>
77bb7e9b1SStephen Boyd #include <linux/usb/chipidea.h>
87bb7e9b1SStephen Boyd #include <linux/ulpi/interface.h>
97bb7e9b1SStephen Boyd 
107bb7e9b1SStephen Boyd #include "ci.h"
117bb7e9b1SStephen Boyd 
127bb7e9b1SStephen Boyd #define ULPI_WAKEUP		BIT(31)
137bb7e9b1SStephen Boyd #define ULPI_RUN		BIT(30)
147bb7e9b1SStephen Boyd #define ULPI_WRITE		BIT(29)
157bb7e9b1SStephen Boyd #define ULPI_SYNC_STATE		BIT(27)
167bb7e9b1SStephen Boyd #define ULPI_ADDR(n)		((n) << 16)
177bb7e9b1SStephen Boyd #define ULPI_DATA(n)		(n)
187bb7e9b1SStephen Boyd 
197bb7e9b1SStephen Boyd static int ci_ulpi_wait(struct ci_hdrc *ci, u32 mask)
207bb7e9b1SStephen Boyd {
217bb7e9b1SStephen Boyd 	unsigned long usec = 10000;
227bb7e9b1SStephen Boyd 
237bb7e9b1SStephen Boyd 	while (usec--) {
247bb7e9b1SStephen Boyd 		if (!hw_read(ci, OP_ULPI_VIEWPORT, mask))
257bb7e9b1SStephen Boyd 			return 0;
267bb7e9b1SStephen Boyd 
277bb7e9b1SStephen Boyd 		udelay(1);
287bb7e9b1SStephen Boyd 	}
297bb7e9b1SStephen Boyd 
307bb7e9b1SStephen Boyd 	return -ETIMEDOUT;
317bb7e9b1SStephen Boyd }
327bb7e9b1SStephen Boyd 
337bb7e9b1SStephen Boyd static int ci_ulpi_read(struct device *dev, u8 addr)
347bb7e9b1SStephen Boyd {
357bb7e9b1SStephen Boyd 	struct ci_hdrc *ci = dev_get_drvdata(dev);
367bb7e9b1SStephen Boyd 	int ret;
377bb7e9b1SStephen Boyd 
387bb7e9b1SStephen Boyd 	hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
397bb7e9b1SStephen Boyd 	ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
407bb7e9b1SStephen Boyd 	if (ret)
417bb7e9b1SStephen Boyd 		return ret;
427bb7e9b1SStephen Boyd 
437bb7e9b1SStephen Boyd 	hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_RUN | ULPI_ADDR(addr));
447bb7e9b1SStephen Boyd 	ret = ci_ulpi_wait(ci, ULPI_RUN);
457bb7e9b1SStephen Boyd 	if (ret)
467bb7e9b1SStephen Boyd 		return ret;
477bb7e9b1SStephen Boyd 
487bb7e9b1SStephen Boyd 	return hw_read(ci, OP_ULPI_VIEWPORT, GENMASK(15, 8)) >> 8;
497bb7e9b1SStephen Boyd }
507bb7e9b1SStephen Boyd 
517bb7e9b1SStephen Boyd static int ci_ulpi_write(struct device *dev, u8 addr, u8 val)
527bb7e9b1SStephen Boyd {
537bb7e9b1SStephen Boyd 	struct ci_hdrc *ci = dev_get_drvdata(dev);
547bb7e9b1SStephen Boyd 	int ret;
557bb7e9b1SStephen Boyd 
567bb7e9b1SStephen Boyd 	hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
577bb7e9b1SStephen Boyd 	ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
587bb7e9b1SStephen Boyd 	if (ret)
597bb7e9b1SStephen Boyd 		return ret;
607bb7e9b1SStephen Boyd 
617bb7e9b1SStephen Boyd 	hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff,
627bb7e9b1SStephen Boyd 		 ULPI_RUN | ULPI_WRITE | ULPI_ADDR(addr) | val);
637bb7e9b1SStephen Boyd 	return ci_ulpi_wait(ci, ULPI_RUN);
647bb7e9b1SStephen Boyd }
657bb7e9b1SStephen Boyd 
667bb7e9b1SStephen Boyd int ci_ulpi_init(struct ci_hdrc *ci)
677bb7e9b1SStephen Boyd {
687bb7e9b1SStephen Boyd 	if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
697bb7e9b1SStephen Boyd 		return 0;
707bb7e9b1SStephen Boyd 
71*718d4a63SPeter Chen 	/*
72*718d4a63SPeter Chen 	 * Set PORTSC correctly so we can read/write ULPI registers for
73*718d4a63SPeter Chen 	 * identification purposes
74*718d4a63SPeter Chen 	 */
75*718d4a63SPeter Chen 	hw_phymode_configure(ci);
767bb7e9b1SStephen Boyd 
777bb7e9b1SStephen Boyd 	ci->ulpi_ops.read = ci_ulpi_read;
787bb7e9b1SStephen Boyd 	ci->ulpi_ops.write = ci_ulpi_write;
797bb7e9b1SStephen Boyd 	ci->ulpi = ulpi_register_interface(ci->dev, &ci->ulpi_ops);
807bb7e9b1SStephen Boyd 	if (IS_ERR(ci->ulpi))
817bb7e9b1SStephen Boyd 		dev_err(ci->dev, "failed to register ULPI interface");
827bb7e9b1SStephen Boyd 
837bb7e9b1SStephen Boyd 	return PTR_ERR_OR_ZERO(ci->ulpi);
847bb7e9b1SStephen Boyd }
857bb7e9b1SStephen Boyd 
867bb7e9b1SStephen Boyd void ci_ulpi_exit(struct ci_hdrc *ci)
877bb7e9b1SStephen Boyd {
887bb7e9b1SStephen Boyd 	if (ci->ulpi) {
897bb7e9b1SStephen Boyd 		ulpi_unregister_interface(ci->ulpi);
907bb7e9b1SStephen Boyd 		ci->ulpi = NULL;
917bb7e9b1SStephen Boyd 	}
927bb7e9b1SStephen Boyd }
937bb7e9b1SStephen Boyd 
947bb7e9b1SStephen Boyd int ci_ulpi_resume(struct ci_hdrc *ci)
957bb7e9b1SStephen Boyd {
967bb7e9b1SStephen Boyd 	int cnt = 100000;
977bb7e9b1SStephen Boyd 
98a930d8bdSFabio Estevam 	if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
99a930d8bdSFabio Estevam 		return 0;
100a930d8bdSFabio Estevam 
1017bb7e9b1SStephen Boyd 	while (cnt-- > 0) {
1027bb7e9b1SStephen Boyd 		if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE))
1037bb7e9b1SStephen Boyd 			return 0;
1047bb7e9b1SStephen Boyd 		udelay(1);
1057bb7e9b1SStephen Boyd 	}
1067bb7e9b1SStephen Boyd 
1077bb7e9b1SStephen Boyd 	return -ETIMEDOUT;
1087bb7e9b1SStephen Boyd }
109