Home | History | Annotate | Download | only in phy
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (C) 2018 lvaro Fernndez Rojas <noltari (at) gmail.com>
      4  *
      5  * Derived from linux/arch/mips/bcm63xx/usb-common.c:
      6  *	Copyright 2008 Maxime Bizon <mbizon (at) freebox.fr>
      7  *	Copyright 2013 Florian Fainelli <florian (at) openwrt.org>
      8  */
      9 
     10 #include <common.h>
     11 #include <clk.h>
     12 #include <dm.h>
     13 #include <generic-phy.h>
     14 #include <power-domain.h>
     15 #include <reset.h>
     16 #include <asm/io.h>
     17 #include <dm/device.h>
     18 
     19 /* USBH PLL Control register */
     20 #define USBH_PLL_REG		0x18
     21 #define USBH_PLL_IDDQ_PWRDN	BIT(9)
     22 #define USBH_PLL_PWRDN_DELAY	BIT(10)
     23 
     24 /* USBH Swap Control register */
     25 #define USBH_SWAP_REG		0x1c
     26 #define USBH_SWAP_OHCI_DATA	BIT(0)
     27 #define USBH_SWAP_OHCI_ENDIAN	BIT(1)
     28 #define USBH_SWAP_EHCI_DATA	BIT(3)
     29 #define USBH_SWAP_EHCI_ENDIAN	BIT(4)
     30 
     31 /* USBH Setup register */
     32 #define USBH_SETUP_REG		0x28
     33 #define USBH_SETUP_IOC		BIT(4)
     34 #define USBH_SETUP_IPP		BIT(5)
     35 
     36 struct bcm6368_usbh_hw {
     37 	uint32_t setup_clr;
     38 	uint32_t pll_clr;
     39 };
     40 
     41 struct bcm6368_usbh_priv {
     42 	const struct bcm6368_usbh_hw *hw;
     43 	void __iomem *regs;
     44 };
     45 
     46 static int bcm6368_usbh_init(struct phy *phy)
     47 {
     48 	struct bcm6368_usbh_priv *priv = dev_get_priv(phy->dev);
     49 	const struct bcm6368_usbh_hw *hw = priv->hw;
     50 
     51 	/* configure to work in native cpu endian */
     52 	clrsetbits_be32(priv->regs + USBH_SWAP_REG,
     53 			USBH_SWAP_EHCI_ENDIAN | USBH_SWAP_OHCI_ENDIAN,
     54 			USBH_SWAP_EHCI_DATA | USBH_SWAP_OHCI_DATA);
     55 
     56 	/* setup config */
     57 	if (hw->setup_clr)
     58 		clrbits_be32(priv->regs + USBH_SETUP_REG, hw->setup_clr);
     59 
     60 	setbits_be32(priv->regs + USBH_SETUP_REG, USBH_SETUP_IOC);
     61 
     62 	/* enable pll control */
     63 	if (hw->pll_clr)
     64 		clrbits_be32(priv->regs + USBH_PLL_REG, hw->pll_clr);
     65 
     66 	return 0;
     67 }
     68 
     69 static struct phy_ops bcm6368_usbh_ops = {
     70 	.init = bcm6368_usbh_init,
     71 };
     72 
     73 static const struct bcm6368_usbh_hw bcm6328_hw = {
     74 	.pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY,
     75 	.setup_clr = 0,
     76 };
     77 
     78 static const struct bcm6368_usbh_hw bcm6362_hw = {
     79 	.pll_clr = 0,
     80 	.setup_clr = 0,
     81 };
     82 
     83 static const struct bcm6368_usbh_hw bcm6368_hw = {
     84 	.pll_clr = 0,
     85 	.setup_clr = 0,
     86 };
     87 
     88 static const struct bcm6368_usbh_hw bcm63268_hw = {
     89 	.pll_clr = USBH_PLL_IDDQ_PWRDN | USBH_PLL_PWRDN_DELAY,
     90 	.setup_clr = USBH_SETUP_IPP,
     91 };
     92 
     93 static const struct udevice_id bcm6368_usbh_ids[] = {
     94 	{
     95 		.compatible = "brcm,bcm6328-usbh",
     96 		.data = (ulong)&bcm6328_hw,
     97 	}, {
     98 		.compatible = "brcm,bcm6362-usbh",
     99 		.data = (ulong)&bcm6362_hw,
    100 	}, {
    101 		.compatible = "brcm,bcm6368-usbh",
    102 		.data = (ulong)&bcm6368_hw,
    103 	}, {
    104 		.compatible = "brcm,bcm63268-usbh",
    105 		.data = (ulong)&bcm63268_hw,
    106 	}, { /* sentinel */ }
    107 };
    108 
    109 static int bcm6368_usbh_probe(struct udevice *dev)
    110 {
    111 	struct bcm6368_usbh_priv *priv = dev_get_priv(dev);
    112 	const struct bcm6368_usbh_hw *hw =
    113 		(const struct bcm6368_usbh_hw *)dev_get_driver_data(dev);
    114 #if defined(CONFIG_POWER_DOMAIN)
    115 	struct power_domain pwr_dom;
    116 #endif
    117 	struct reset_ctl rst_ctl;
    118 	struct clk clk;
    119 	int ret;
    120 
    121 	priv->regs = dev_remap_addr(dev);
    122 	if (!priv->regs)
    123 		return -EINVAL;
    124 
    125 	priv->hw = hw;
    126 
    127 	/* enable usbh clock */
    128 	ret = clk_get_by_name(dev, "usbh", &clk);
    129 	if (ret < 0)
    130 		return ret;
    131 
    132 	ret = clk_enable(&clk);
    133 	if (ret < 0)
    134 		return ret;
    135 
    136 	ret = clk_free(&clk);
    137 	if (ret < 0)
    138 		return ret;
    139 
    140 #if defined(CONFIG_POWER_DOMAIN)
    141 	/* enable power domain */
    142 	ret = power_domain_get(dev, &pwr_dom);
    143 	if (ret < 0)
    144 		return ret;
    145 
    146 	ret = power_domain_on(&pwr_dom);
    147 	if (ret < 0)
    148 		return ret;
    149 
    150 	ret = power_domain_free(&pwr_dom);
    151 	if (ret < 0)
    152 		return ret;
    153 #endif
    154 
    155 	/* perform reset */
    156 	ret = reset_get_by_index(dev, 0, &rst_ctl);
    157 	if (ret < 0)
    158 		return ret;
    159 
    160 	ret = reset_deassert(&rst_ctl);
    161 	if (ret < 0)
    162 		return ret;
    163 
    164 	ret = reset_free(&rst_ctl);
    165 	if (ret < 0)
    166 		return ret;
    167 
    168 	/* enable usb_ref clock */
    169 	ret = clk_get_by_name(dev, "usb_ref", &clk);
    170 	if (!ret) {
    171 		ret = clk_enable(&clk);
    172 		if (ret < 0)
    173 			return ret;
    174 
    175 		ret = clk_free(&clk);
    176 		if (ret < 0)
    177 			return ret;
    178 	}
    179 
    180 	mdelay(100);
    181 
    182 	return 0;
    183 }
    184 
    185 U_BOOT_DRIVER(bcm6368_usbh) = {
    186 	.name = "bcm6368-usbh",
    187 	.id = UCLASS_PHY,
    188 	.of_match = bcm6368_usbh_ids,
    189 	.ops = &bcm6368_usbh_ops,
    190 	.priv_auto_alloc_size = sizeof(struct bcm6368_usbh_priv),
    191 	.probe = bcm6368_usbh_probe,
    192 };
    193