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>
190e00a84cSMasahiro 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
31*143fc13bSJean-Jacques Hiblot #include <usb/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
xhci_usb_ofdata_to_platdata(struct udevice * dev)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
exynos5_usb3_phy_init(struct exynos_usb3_phy * phy)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
exynos5_usb3_phy_exit(struct exynos_usb3_phy * phy)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
exynos_xhci_core_init(struct exynos_xhci * exynos)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
exynos_xhci_core_exit(struct exynos_xhci * exynos)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
xhci_usb_probe(struct udevice * dev)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
xhci_usb_remove(struct udevice * dev)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