Commit 976f82e8 authored by Ladislav Michl's avatar Ladislav Michl Committed by Greg Kroah-Hartman
Browse files

usb: dwc3: dwc3-octeon: Convert to glue driver



DWC3 as implemented in Cavium SoC is using UCTL bridge unit
between I/O interconnect and USB controller.

Currently there is no bond with dwc3 core code, so if anything goes
wrong in UCTL setup dwc3 is left in reset, which leads to bus error
while trying to read any device register. Thus any failure in UCTL
initialization ends with kernel panic.

To avoid this move Octeon DWC3 glue code from arch/mips and make it
proper glue driver which is used instead of dwc3-of-simple.

Signed-off-by: default avatarLadislav Michl <ladis@linux-mips.org>
Acked-by: default avatarThomas Bogendoerfer <tsbogend@alpha.franken.de>
Acked-by: default avatarThinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/ZMd/ReyiY7wS6DvN@lenoch


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b35935d6
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -18,4 +18,3 @@ obj-y += crypto/
obj-$(CONFIG_MTD)		      += flash_setup.o
obj-$(CONFIG_SMP)		      += smp.o
obj-$(CONFIG_OCTEON_ILM)	      += oct_ilm.o
obj-$(CONFIG_USB)		      += octeon-usb.o
+0 −1
Original line number Diff line number Diff line
@@ -450,7 +450,6 @@ static const struct of_device_id octeon_ids[] __initconst = {
	{ .compatible = "cavium,octeon-3860-bootbus", },
	{ .compatible = "cavium,mdio-mux", },
	{ .compatible = "gpio-leds", },
	{ .compatible = "cavium,octeon-7130-usb-uctl", },
	{},
};

+10 −0
Original line number Diff line number Diff line
@@ -168,4 +168,14 @@ config USB_DWC3_AM62
	  The Designware Core USB3 IP is programmed to operate in
	  in USB 2.0 mode only.
	  Say 'Y' or 'M' here if you have one such device

config USB_DWC3_OCTEON
	tristate "Cavium Octeon Platforms"
	depends on CAVIUM_OCTEON_SOC || COMPILE_TEST
	default USB_DWC3
	help
	  Support Cavium Octeon platforms with DesignWare Core USB3 IP.
	  Only the host mode is currently supported.
	  Say 'Y' or 'M' here if you have one such device.

endif
+1 −0
Original line number Diff line number Diff line
@@ -54,3 +54,4 @@ obj-$(CONFIG_USB_DWC3_ST) += dwc3-st.o
obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
obj-$(CONFIG_USB_DWC3_IMX8MP)		+= dwc3-imx8mp.o
obj-$(CONFIG_USB_DWC3_XILINX)		+= dwc3-xilinx.o
obj-$(CONFIG_USB_DWC3_OCTEON)		+= dwc3-octeon.o
+57 −48
Original line number Diff line number Diff line
@@ -187,7 +187,10 @@
#define USBDRD_UCTL_ECC				0xf0
#define USBDRD_UCTL_SPARE1			0xf8

static DEFINE_MUTEX(dwc3_octeon_clocks_mutex);
struct dwc3_octeon {
	struct device *dev;
	void __iomem *base;
};

#ifdef CONFIG_CAVIUM_OCTEON_SOC
#include <asm/octeon/octeon.h>
@@ -233,6 +236,11 @@ static inline uint64_t dwc3_octeon_readq(void __iomem *addr)
static inline void dwc3_octeon_writeq(void __iomem *base, uint64_t val) { }

static inline void dwc3_octeon_config_gpio(int index, int gpio) { }

static uint64_t octeon_get_io_clock_rate(void)
{
	return 150000000;
}
#endif

static int dwc3_octeon_get_divider(void)
@@ -494,58 +502,59 @@ static void __init dwc3_octeon_phy_reset(void __iomem *base)
	dwc3_octeon_writeq(uctl_ctl_reg, val);
}

static int __init dwc3_octeon_device_init(void)
static int dwc3_octeon_probe(struct platform_device *pdev)
{
	const char compat_node_name[] = "cavium,octeon-7130-usb-uctl";
	struct platform_device *pdev;
	struct device_node *node;
	struct resource *res;
	void __iomem *base;
	struct device *dev = &pdev->dev;
	struct device_node *node = dev->of_node;
	struct dwc3_octeon *octeon;
	int err;

	/*
	 * There should only be three universal controllers, "uctl"
	 * in the device tree. Two USB and a SATA, which we ignore.
	 */
	node = NULL;
	do {
		node = of_find_node_by_name(node, "uctl");
		if (!node)
			return -ENODEV;
	octeon = devm_kzalloc(dev, sizeof(*octeon), GFP_KERNEL);
	if (!octeon)
		return -ENOMEM;

		if (of_device_is_compatible(node, compat_node_name)) {
			pdev = of_find_device_by_node(node);
			if (!pdev)
				return -ENODEV;
	octeon->dev = dev;
	octeon->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(octeon->base))
		return PTR_ERR(octeon->base);

			/*
			 * The code below maps in the registers necessary for
			 * setting up the clocks and reseting PHYs. We must
			 * release the resources so the dwc3 subsystem doesn't
			 * know the difference.
			 */
			base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
			if (IS_ERR(base)) {
				put_device(&pdev->dev);
				return PTR_ERR(base);
			}
	err = dwc3_octeon_clocks_start(dev, octeon->base);
	if (err)
		return err;

	dwc3_octeon_set_endian_mode(octeon->base);
	dwc3_octeon_phy_reset(octeon->base);

			mutex_lock(&dwc3_octeon_clocks_mutex);
			if (dwc3_octeon_clocks_start(&pdev->dev, base) == 0)
				dev_info(&pdev->dev, "clocks initialized.\n");
			dwc3_octeon_set_endian_mode(base);
			dwc3_octeon_phy_reset(base);
			mutex_unlock(&dwc3_octeon_clocks_mutex);
			devm_iounmap(&pdev->dev, base);
			devm_release_mem_region(&pdev->dev, res->start,
						resource_size(res));
			put_device(&pdev->dev);
	platform_set_drvdata(pdev, octeon);

	return of_platform_populate(node, NULL, NULL, dev);
}
	} while (node != NULL);

	return 0;
static void dwc3_octeon_remove(struct platform_device *pdev)
{
	struct dwc3_octeon *octeon = platform_get_drvdata(pdev);

	of_platform_depopulate(octeon->dev);
	platform_set_drvdata(pdev, NULL);
}
device_initcall(dwc3_octeon_device_init);

static const struct of_device_id dwc3_octeon_of_match[] = {
	{ .compatible = "cavium,octeon-7130-usb-uctl" },
	{ },
};
MODULE_DEVICE_TABLE(of, dwc3_octeon_of_match);

static struct platform_driver dwc3_octeon_driver = {
	.probe		= dwc3_octeon_probe,
	.remove_new	= dwc3_octeon_remove,
	.driver		= {
		.name	= "dwc3-octeon",
		.of_match_table = dwc3_octeon_of_match,
	},
};
module_platform_driver(dwc3_octeon_driver);

MODULE_ALIAS("platform:dwc3-octeon");
MODULE_AUTHOR("David Daney <david.daney@cavium.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("USB driver for OCTEON III SoC");
MODULE_DESCRIPTION("DesignWare USB3 OCTEON III Glue Layer");
Loading