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