xref: /openbmc/u-boot/drivers/usb/host/xhci-exynos5.c (revision e8f80a5a)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
213194f3bSVivek Gautam /*
313194f3bSVivek Gautam  * SAMSUNG EXYNOS5 USB HOST XHCI Controller
413194f3bSVivek Gautam  *
513194f3bSVivek Gautam  * Copyright (C) 2012 Samsung Electronics Co.Ltd
613194f3bSVivek Gautam  *	Vivek Gautam <gautam.vivek@samsung.com>
713194f3bSVivek Gautam  *	Vikas Sajjan <vikas.sajjan@samsung.com>
813194f3bSVivek Gautam  */
913194f3bSVivek Gautam 
1013194f3bSVivek Gautam /*
1113194f3bSVivek Gautam  * This file is a conglomeration for DWC3-init sequence and further
1213194f3bSVivek Gautam  * exynos5 specific PHY-init sequence.
1313194f3bSVivek Gautam  */
1413194f3bSVivek Gautam 
1513194f3bSVivek Gautam #include <common.h>
1652e69357SSimon Glass #include <dm.h>
1713194f3bSVivek Gautam #include <fdtdec.h>
18b08c8c48SMasahiro Yamada #include <linux/libfdt.h>
1913194f3bSVivek Gautam #include <malloc.h>
2013194f3bSVivek Gautam #include <usb.h>
2113194f3bSVivek Gautam #include <watchdog.h>
2213194f3bSVivek Gautam #include <asm/arch/cpu.h>
2313194f3bSVivek Gautam #include <asm/arch/power.h>
2413194f3bSVivek Gautam #include <asm/arch/xhci-exynos.h>
254a271cb1SJulius Werner #include <asm/gpio.h>
265d97dff0SMasahiro Yamada #include <linux/errno.h>
2713194f3bSVivek Gautam #include <linux/compat.h>
2813194f3bSVivek Gautam #include <linux/usb/dwc3.h>
2913194f3bSVivek Gautam 
3013194f3bSVivek Gautam #include "xhci.h"
3113194f3bSVivek Gautam 
3213194f3bSVivek Gautam /* Declare global data pointer */
3313194f3bSVivek Gautam DECLARE_GLOBAL_DATA_PTR;
3413194f3bSVivek Gautam 
3552e69357SSimon Glass struct exynos_xhci_platdata {
3652e69357SSimon Glass 	fdt_addr_t hcd_base;
3752e69357SSimon Glass 	fdt_addr_t phy_base;
3852e69357SSimon Glass 	struct gpio_desc vbus_gpio;
3952e69357SSimon Glass };
4052e69357SSimon Glass 
4113194f3bSVivek Gautam /**
4213194f3bSVivek Gautam  * Contains pointers to register base addresses
4313194f3bSVivek Gautam  * for the usb controller.
4413194f3bSVivek Gautam  */
4513194f3bSVivek Gautam struct exynos_xhci {
4652e69357SSimon Glass 	struct usb_platdata usb_plat;
4752e69357SSimon Glass 	struct xhci_ctrl ctrl;
4813194f3bSVivek Gautam 	struct exynos_usb3_phy *usb3_phy;
4913194f3bSVivek Gautam 	struct xhci_hccr *hcd;
5013194f3bSVivek Gautam 	struct dwc3 *dwc3_reg;
5113194f3bSVivek Gautam };
5213194f3bSVivek Gautam 
xhci_usb_ofdata_to_platdata(struct udevice * dev)5352e69357SSimon Glass static int xhci_usb_ofdata_to_platdata(struct udevice *dev)
5452e69357SSimon Glass {
5552e69357SSimon Glass 	struct exynos_xhci_platdata *plat = dev_get_platdata(dev);
5652e69357SSimon Glass 	const void *blob = gd->fdt_blob;
5752e69357SSimon Glass 	unsigned int node;
5852e69357SSimon Glass 	int depth;
5952e69357SSimon Glass 
6052e69357SSimon Glass 	/*
6152e69357SSimon Glass 	 * Get the base address for XHCI controller from the device node
6252e69357SSimon Glass 	 */
63a821c4afSSimon Glass 	plat->hcd_base = devfdt_get_addr(dev);
6452e69357SSimon Glass 	if (plat->hcd_base == FDT_ADDR_T_NONE) {
6552e69357SSimon Glass 		debug("Can't get the XHCI register base address\n");
6652e69357SSimon Glass 		return -ENXIO;
6752e69357SSimon Glass 	}
6852e69357SSimon Glass 
6952e69357SSimon Glass 	depth = 0;
70e160f7d4SSimon Glass 	node = fdtdec_next_compatible_subnode(blob, dev_of_offset(dev),
7152e69357SSimon Glass 				COMPAT_SAMSUNG_EXYNOS5_USB3_PHY, &depth);
7252e69357SSimon Glass 	if (node <= 0) {
7352e69357SSimon Glass 		debug("XHCI: Can't get device node for usb3-phy controller\n");
7452e69357SSimon Glass 		return -ENODEV;
7552e69357SSimon Glass 	}
7652e69357SSimon Glass 
7752e69357SSimon Glass 	/*
7852e69357SSimon Glass 	 * Get the base address for usbphy from the device node
7952e69357SSimon Glass 	 */
8052e69357SSimon Glass 	plat->phy_base = fdtdec_get_addr(blob, node, "reg");
8152e69357SSimon Glass 	if (plat->phy_base == FDT_ADDR_T_NONE) {
8252e69357SSimon Glass 		debug("Can't get the usbphy register address\n");
8352e69357SSimon Glass 		return -ENXIO;
8452e69357SSimon Glass 	}
8552e69357SSimon Glass 
8652e69357SSimon Glass 	/* Vbus gpio */
8752e69357SSimon Glass 	gpio_request_by_name(dev, "samsung,vbus-gpio", 0,
8852e69357SSimon Glass 			     &plat->vbus_gpio, GPIOD_IS_OUT);
8952e69357SSimon Glass 
9052e69357SSimon Glass 	return 0;
9152e69357SSimon Glass }
9213194f3bSVivek Gautam 
exynos5_usb3_phy_init(struct exynos_usb3_phy * phy)9313194f3bSVivek Gautam static void exynos5_usb3_phy_init(struct exynos_usb3_phy *phy)
9413194f3bSVivek Gautam {
9513194f3bSVivek Gautam 	u32 reg;
9613194f3bSVivek Gautam 
9713194f3bSVivek Gautam 	/* enabling usb_drd phy */
9813194f3bSVivek Gautam 	set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_EN);
9913194f3bSVivek Gautam 
10013194f3bSVivek Gautam 	/* Reset USB 3.0 PHY */
10113194f3bSVivek Gautam 	writel(0x0, &phy->phy_reg0);
10213194f3bSVivek Gautam 
10313194f3bSVivek Gautam 	clrbits_le32(&phy->phy_param0,
10413194f3bSVivek Gautam 			/* Select PHY CLK source */
10513194f3bSVivek Gautam 			PHYPARAM0_REF_USE_PAD |
10613194f3bSVivek Gautam 			/* Set Loss-of-Signal Detector sensitivity */
10713194f3bSVivek Gautam 			PHYPARAM0_REF_LOSLEVEL_MASK);
10813194f3bSVivek Gautam 	setbits_le32(&phy->phy_param0, PHYPARAM0_REF_LOSLEVEL);
10913194f3bSVivek Gautam 
11013194f3bSVivek Gautam 	writel(0x0, &phy->phy_resume);
11113194f3bSVivek Gautam 
11213194f3bSVivek Gautam 	/*
11313194f3bSVivek Gautam 	 * Setting the Frame length Adj value[6:1] to default 0x20
11413194f3bSVivek Gautam 	 * See xHCI 1.0 spec, 5.2.4
11513194f3bSVivek Gautam 	 */
11613194f3bSVivek Gautam 	setbits_le32(&phy->link_system,
11713194f3bSVivek Gautam 			LINKSYSTEM_XHCI_VERSION_CONTROL |
11813194f3bSVivek Gautam 			LINKSYSTEM_FLADJ(0x20));
11913194f3bSVivek Gautam 
12013194f3bSVivek Gautam 	/* Set Tx De-Emphasis level */
12113194f3bSVivek Gautam 	clrbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH_MASK);
12213194f3bSVivek Gautam 	setbits_le32(&phy->phy_param1, PHYPARAM1_PCS_TXDEEMPH);
12313194f3bSVivek Gautam 
12413194f3bSVivek Gautam 	setbits_le32(&phy->phy_batchg, PHYBATCHG_UTMI_CLKSEL);
12513194f3bSVivek Gautam 
12613194f3bSVivek Gautam 	/* PHYTEST POWERDOWN Control */
12713194f3bSVivek Gautam 	clrbits_le32(&phy->phy_test,
12813194f3bSVivek Gautam 			PHYTEST_POWERDOWN_SSP |
12913194f3bSVivek Gautam 			PHYTEST_POWERDOWN_HSP);
13013194f3bSVivek Gautam 
13113194f3bSVivek Gautam 	/* UTMI Power Control */
13213194f3bSVivek Gautam 	writel(PHYUTMI_OTGDISABLE, &phy->phy_utmi);
13313194f3bSVivek Gautam 
13413194f3bSVivek Gautam 		/* Use core clock from main PLL */
13513194f3bSVivek Gautam 	reg = PHYCLKRST_REFCLKSEL_EXT_REFCLK |
13613194f3bSVivek Gautam 		/* Default 24Mhz crystal clock */
13713194f3bSVivek Gautam 		PHYCLKRST_FSEL(FSEL_CLKSEL_24M) |
13813194f3bSVivek Gautam 		PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
13913194f3bSVivek Gautam 		PHYCLKRST_SSC_REFCLKSEL(0x88) |
14013194f3bSVivek Gautam 		/* Force PortReset of PHY */
14113194f3bSVivek Gautam 		PHYCLKRST_PORTRESET |
14213194f3bSVivek Gautam 		/* Digital power supply in normal operating mode */
14313194f3bSVivek Gautam 		PHYCLKRST_RETENABLEN |
14413194f3bSVivek Gautam 		/* Enable ref clock for SS function */
14513194f3bSVivek Gautam 		PHYCLKRST_REF_SSP_EN |
14613194f3bSVivek Gautam 		/* Enable spread spectrum */
14713194f3bSVivek Gautam 		PHYCLKRST_SSC_EN |
14813194f3bSVivek Gautam 		/* Power down HS Bias and PLL blocks in suspend mode */
14913194f3bSVivek Gautam 		PHYCLKRST_COMMONONN;
15013194f3bSVivek Gautam 
15113194f3bSVivek Gautam 	writel(reg, &phy->phy_clk_rst);
15213194f3bSVivek Gautam 
15313194f3bSVivek Gautam 	/* giving time to Phy clock to settle before resetting */
15413194f3bSVivek Gautam 	udelay(10);
15513194f3bSVivek Gautam 
15613194f3bSVivek Gautam 	reg &= ~PHYCLKRST_PORTRESET;
15713194f3bSVivek Gautam 	writel(reg, &phy->phy_clk_rst);
15813194f3bSVivek Gautam }
15913194f3bSVivek Gautam 
exynos5_usb3_phy_exit(struct exynos_usb3_phy * phy)16013194f3bSVivek Gautam static void exynos5_usb3_phy_exit(struct exynos_usb3_phy *phy)
16113194f3bSVivek Gautam {
16213194f3bSVivek Gautam 	setbits_le32(&phy->phy_utmi,
16313194f3bSVivek Gautam 			PHYUTMI_OTGDISABLE |
16413194f3bSVivek Gautam 			PHYUTMI_FORCESUSPEND |
16513194f3bSVivek Gautam 			PHYUTMI_FORCESLEEP);
16613194f3bSVivek Gautam 
16713194f3bSVivek Gautam 	clrbits_le32(&phy->phy_clk_rst,
16813194f3bSVivek Gautam 			PHYCLKRST_REF_SSP_EN |
16913194f3bSVivek Gautam 			PHYCLKRST_SSC_EN |
17013194f3bSVivek Gautam 			PHYCLKRST_COMMONONN);
17113194f3bSVivek Gautam 
17213194f3bSVivek Gautam 	/* PHYTEST POWERDOWN Control to remove leakage current */
17313194f3bSVivek Gautam 	setbits_le32(&phy->phy_test,
17413194f3bSVivek Gautam 			PHYTEST_POWERDOWN_SSP |
17513194f3bSVivek Gautam 			PHYTEST_POWERDOWN_HSP);
17613194f3bSVivek Gautam 
17713194f3bSVivek Gautam 	/* disabling usb_drd phy */
17813194f3bSVivek Gautam 	set_usbdrd_phy_ctrl(POWER_USB_DRD_PHY_CTRL_DISABLE);
17913194f3bSVivek Gautam }
18013194f3bSVivek Gautam 
exynos_xhci_core_init(struct exynos_xhci * exynos)18113194f3bSVivek Gautam static int exynos_xhci_core_init(struct exynos_xhci *exynos)
18213194f3bSVivek Gautam {
18313194f3bSVivek Gautam 	int ret;
18413194f3bSVivek Gautam 
18513194f3bSVivek Gautam 	exynos5_usb3_phy_init(exynos->usb3_phy);
18613194f3bSVivek Gautam 
18713194f3bSVivek Gautam 	ret = dwc3_core_init(exynos->dwc3_reg);
18813194f3bSVivek Gautam 	if (ret) {
18913194f3bSVivek Gautam 		debug("failed to initialize core\n");
19013194f3bSVivek Gautam 		return -EINVAL;
19113194f3bSVivek Gautam 	}
19213194f3bSVivek Gautam 
19313194f3bSVivek Gautam 	/* We are hard-coding DWC3 core to Host Mode */
19413194f3bSVivek Gautam 	dwc3_set_mode(exynos->dwc3_reg, DWC3_GCTL_PRTCAP_HOST);
19513194f3bSVivek Gautam 
19613194f3bSVivek Gautam 	return 0;
19713194f3bSVivek Gautam }
19813194f3bSVivek Gautam 
exynos_xhci_core_exit(struct exynos_xhci * exynos)19913194f3bSVivek Gautam static void exynos_xhci_core_exit(struct exynos_xhci *exynos)
20013194f3bSVivek Gautam {
20113194f3bSVivek Gautam 	exynos5_usb3_phy_exit(exynos->usb3_phy);
20213194f3bSVivek Gautam }
20313194f3bSVivek Gautam 
xhci_usb_probe(struct udevice * dev)20452e69357SSimon Glass static int xhci_usb_probe(struct udevice *dev)
20552e69357SSimon Glass {
20652e69357SSimon Glass 	struct exynos_xhci_platdata *plat = dev_get_platdata(dev);
20752e69357SSimon Glass 	struct exynos_xhci *ctx = dev_get_priv(dev);
20852e69357SSimon Glass 	struct xhci_hcor *hcor;
20952e69357SSimon Glass 	int ret;
21052e69357SSimon Glass 
21152e69357SSimon Glass 	ctx->hcd = (struct xhci_hccr *)plat->hcd_base;
21252e69357SSimon Glass 	ctx->usb3_phy = (struct exynos_usb3_phy *)plat->phy_base;
21352e69357SSimon Glass 	ctx->dwc3_reg = (struct dwc3 *)((char *)(ctx->hcd) + DWC3_REG_OFFSET);
21452e69357SSimon Glass 	hcor = (struct xhci_hcor *)((uint32_t)ctx->hcd +
21552e69357SSimon Glass 			HC_LENGTH(xhci_readl(&ctx->hcd->cr_capbase)));
21652e69357SSimon Glass 
21752e69357SSimon Glass 	/* setup the Vbus gpio here */
21852e69357SSimon Glass 	if (dm_gpio_is_valid(&plat->vbus_gpio))
21952e69357SSimon Glass 		dm_gpio_set_value(&plat->vbus_gpio, 1);
22052e69357SSimon Glass 
22152e69357SSimon Glass 	ret = exynos_xhci_core_init(ctx);
22252e69357SSimon Glass 	if (ret) {
22352e69357SSimon Glass 		puts("XHCI: failed to initialize controller\n");
22452e69357SSimon Glass 		return -EINVAL;
22552e69357SSimon Glass 	}
22652e69357SSimon Glass 
22752e69357SSimon Glass 	return xhci_register(dev, ctx->hcd, hcor);
22852e69357SSimon Glass }
22952e69357SSimon Glass 
xhci_usb_remove(struct udevice * dev)23052e69357SSimon Glass static int xhci_usb_remove(struct udevice *dev)
23152e69357SSimon Glass {
23252e69357SSimon Glass 	struct exynos_xhci *ctx = dev_get_priv(dev);
23352e69357SSimon Glass 	int ret;
23452e69357SSimon Glass 
23552e69357SSimon Glass 	ret = xhci_deregister(dev);
23652e69357SSimon Glass 	if (ret)
23752e69357SSimon Glass 		return ret;
23852e69357SSimon Glass 	exynos_xhci_core_exit(ctx);
23952e69357SSimon Glass 
24052e69357SSimon Glass 	return 0;
24152e69357SSimon Glass }
24252e69357SSimon Glass 
24352e69357SSimon Glass static const struct udevice_id xhci_usb_ids[] = {
24452e69357SSimon Glass 	{ .compatible = "samsung,exynos5250-xhci" },
24552e69357SSimon Glass 	{ }
24652e69357SSimon Glass };
24752e69357SSimon Glass 
24852e69357SSimon Glass U_BOOT_DRIVER(usb_xhci) = {
24952e69357SSimon Glass 	.name	= "xhci_exynos",
25052e69357SSimon Glass 	.id	= UCLASS_USB,
25152e69357SSimon Glass 	.of_match = xhci_usb_ids,
25252e69357SSimon Glass 	.ofdata_to_platdata = xhci_usb_ofdata_to_platdata,
25352e69357SSimon Glass 	.probe = xhci_usb_probe,
25452e69357SSimon Glass 	.remove = xhci_usb_remove,
25552e69357SSimon Glass 	.ops	= &xhci_usb_ops,
25652e69357SSimon Glass 	.platdata_auto_alloc_size = sizeof(struct exynos_xhci_platdata),
25752e69357SSimon Glass 	.priv_auto_alloc_size = sizeof(struct exynos_xhci),
25852e69357SSimon Glass 	.flags	= DM_FLAG_ALLOC_PRIV_DMA,
25952e69357SSimon Glass };
260