Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 88bc9d19 authored by Heikki Krogerus's avatar Heikki Krogerus Committed by Felipe Balbi
Browse files

usb: dwc3: add ULPI interface support



Registers DWC3's ULPI interface with the ULPI bus when it's
available.

Signed-off-by: default avatarHeikki Krogerus <heikki.krogerus@linux.intel.com>
Acked-by: default avatarDavid Cohen <david.a.cohen@linux.intel.com>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent a89d977c
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -11,6 +11,13 @@ config USB_DWC3

if USB_DWC3

config USB_DWC3_ULPI
	bool "Register ULPI PHY Interface"
	depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_DWC3
	help
	  Select this if you have ULPI type PHY attached to your DWC3
	  controller.

choice
	bool "DWC3 Mode Selection"
	default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET)
+4 −0
Original line number Diff line number Diff line
@@ -15,6 +15,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
	dwc3-y				+= gadget.o ep0.o
endif

ifneq ($(CONFIG_USB_DWC3_ULPI),)
	dwc3-y				+= ulpi.o
endif

ifneq ($(CONFIG_DEBUG_FS),)
	dwc3-y				+= debugfs.o
endif
+31 −3
Original line number Diff line number Diff line
@@ -394,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
/**
 * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
 * @dwc: Pointer to our controller context structure
 *
 * Returns 0 on success. The USB PHY interfaces are configured but not
 * initialized. The PHY interfaces and the PHYs get initialized together with
 * the core in dwc3_core_init.
 */
static void dwc3_phy_setup(struct dwc3 *dwc)
static int dwc3_phy_setup(struct dwc3 *dwc)
{
	u32 reg;
	int ret;

	reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));

@@ -443,13 +448,30 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
	case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI:
		if (!strncmp(dwc->hsphy_interface, "utmi", 4)) {
			reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI;
			break;
		} else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) {
			reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI;
			dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
		} else {
			dev_warn(dwc->dev, "HSPHY Interface not defined\n");

			/* Relying on default value. */
			if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI))
				break;
		}
		/* FALLTHROUGH */
	case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI:
		/* Making sure the interface and PHY are operational */
		ret = dwc3_soft_reset(dwc);
		if (ret)
			return ret;

		udelay(1);

		ret = dwc3_ulpi_init(dwc);
		if (ret)
			return ret;
		/* FALLTHROUGH */
	default:
		break;
	}
@@ -467,6 +489,8 @@ static void dwc3_phy_setup(struct dwc3 *dwc)
		reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;

	dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);

	return 0;
}

/**
@@ -906,7 +930,9 @@ static int dwc3_probe(struct platform_device *pdev)
	platform_set_drvdata(pdev, dwc);
	dwc3_cache_hwparams(dwc);

	dwc3_phy_setup(dwc);
	ret = dwc3_phy_setup(dwc);
	if (ret)
		goto err0;

	ret = dwc3_core_get_phy(dwc);
	if (ret)
@@ -994,6 +1020,7 @@ static int dwc3_probe(struct platform_device *pdev)

err1:
	dwc3_free_event_buffers(dwc);
	dwc3_ulpi_exit(dwc);

err0:
	/*
@@ -1029,6 +1056,7 @@ static int dwc3_remove(struct platform_device *pdev)
	phy_power_off(dwc->usb3_generic_phy);

	dwc3_core_exit(dwc);
	dwc3_ulpi_exit(dwc);

	pm_runtime_put_sync(&pdev->dev);
	pm_runtime_disable(&pdev->dev);
+14 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
#include <linux/ulpi/interface.h>

#include <linux/phy/phy.h>

@@ -661,6 +662,7 @@ struct dwc3_scratchpad_array {
 * @usb3_phy: pointer to USB3 PHY
 * @usb2_generic_phy: pointer to USB2 PHY
 * @usb3_generic_phy: pointer to USB3 PHY
 * @ulpi: pointer to ulpi interface
 * @dcfg: saved contents of DCFG register
 * @gctl: saved contents of GCTL register
 * @isoch_delay: wValue from Set Isochronous Delay request;
@@ -749,6 +751,8 @@ struct dwc3 {
	struct phy		*usb2_generic_phy;
	struct phy		*usb3_generic_phy;

	struct ulpi		*ulpi;

	void __iomem		*regs;
	size_t			regs_size;

@@ -1047,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
}
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */

#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
int dwc3_ulpi_init(struct dwc3 *dwc);
void dwc3_ulpi_exit(struct dwc3 *dwc);
#else
static inline int dwc3_ulpi_init(struct dwc3 *dwc)
{ return 0; }
static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
{ }
#endif

#endif /* __DRIVERS_USB_DWC3_CORE_H */
+91 −0
Original line number Diff line number Diff line
/**
 * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface
 *
 * Copyright (C) 2015 Intel Corporation
 *
 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/ulpi/regs.h>

#include "core.h"
#include "io.h"

#define DWC3_ULPI_ADDR(a) \
		((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \
		DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \
		DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a))

static int dwc3_ulpi_busyloop(struct dwc3 *dwc)
{
	unsigned count = 1000;
	u32 reg;

	while (count--) {
		reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));
		if (!(reg & DWC3_GUSB2PHYACC_BUSY))
			return 0;
		cpu_relax();
	}

	return -ETIMEDOUT;
}

static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr)
{
	struct dwc3 *dwc = dev_get_drvdata(ops->dev);
	u32 reg;
	int ret;

	reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
	dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);

	ret = dwc3_ulpi_busyloop(dwc);
	if (ret)
		return ret;

	reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0));

	return DWC3_GUSB2PHYACC_DATA(reg);
}

static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val)
{
	struct dwc3 *dwc = dev_get_drvdata(ops->dev);
	u32 reg;

	reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr);
	reg |= DWC3_GUSB2PHYACC_WRITE | val;
	dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg);

	return dwc3_ulpi_busyloop(dwc);
}

static struct ulpi_ops dwc3_ulpi_ops = {
	.read = dwc3_ulpi_read,
	.write = dwc3_ulpi_write,
};

int dwc3_ulpi_init(struct dwc3 *dwc)
{
	/* Register the interface */
	dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops);
	if (IS_ERR(dwc->ulpi)) {
		dev_err(dwc->dev, "failed to register ULPI interface");
		return PTR_ERR(dwc->ulpi);
	}

	return 0;
}

void dwc3_ulpi_exit(struct dwc3 *dwc)
{
	if (dwc->ulpi) {
		ulpi_unregister_interface(dwc->ulpi);
		dwc->ulpi = NULL;
	}
}